Accessibility
Enforce WCAG 2.1 AA (keyboard, ARIA, focus, contrast, semantic HTML). Use for accessibility work, pre-ship UI checks, and as a light pass on every UI build or design review — including "a11y", "keyboard", "screen reader", or shipping user-facing changes, without requiring a slash command.From its SKILL.md
npx -y skills add sukirman1901/frontend-expert --skill accessibilityAssembled from the repository path, not quoted from the project. Check it against their README if it does not work.
One thing to look at
- 10 stars10 stars. Stars are a popularity signal and not a quality one, but at this level it is likely that nobody has read this closely except its author, and you would be relying on your own review.
SKILL.md
5.7 KB, as published. Nobody here has run it
Accessibility
Overview
Every user-facing UI must meet WCAG 2.1 AA. Keyboard, labels, contrast, and semantics are required — not optional polish.
When to Use
/designaudits- Light check during
/uibuilds - Dedicated accessibility fixes
Workflow
1. Keyboard
- All interactive elements focusable (native
<button>,<a>, ortabIndex={0}+ key handler) - Logical focus order; visible focus indicators (
focus-visibleoutline) - Focus trapped in modals; restore to trigger on close
- Skip link to main content when long nav exists
Recipe — focus trap (vanilla):
// Trap focus inside a modal
function trapFocus(modal) {
const focusable = modal.querySelectorAll(
'a[href], button:not([disabled]), input, select, textarea, [tabindex]:not([tabindex="-1"])'
);
const first = focusable[0];
const last = focusable[focusable.length - 1];
modal.addEventListener('keydown', (e) => {
if (e.key !== 'Tab') return;
if (e.shiftKey && document.activeElement === first) {
e.preventDefault();
last.focus();
} else if (!e.shiftKey && document.activeElement === last) {
e.preventDefault();
first.focus();
}
});
first?.focus();
}
Recipe — skip link:
<a href="#main-content" class="skip-link">Skip to main content</a>
<!-- In CSS: .skip-link { position: absolute; top: -100%; }
.skip-link:focus { top: 0; z-index: 9999; } -->
<main id="main-content">...</main>
2. ARIA / labels
- Form inputs have visible labels (
<label for="id">or wrapping<label>) - Icon-only buttons have
aria-label - Toggle states use
aria-pressed,aria-expanded,aria-selected - Loading announced via
aria-busy="true"on the loading container - Live regions (
aria-live="polite") for dynamic content updates (toasts, search results count)
Recipe — live region for dynamic count:
<div aria-live="polite" aria-atomic="true" class="sr-only">
<!-- JS updates this when results change -->
<span id="result-count">12 results found</span>
</div>
Recipe — icon button with Reicon:
<button type="button" aria-label="Close dialog">
<re-icon icon="close-circle" size="20" aria-hidden="true"></re-icon>
</button>
3. Color & contrast
- Normal text ≥ 4.5:1 contrast ratio
- Large text (18px+ or 14px+ bold) ≥ 3:1
- Interactive focus indicators ≥ 3:1 against adjacent colors
- Do not rely on color alone for state — add icon, text, or pattern
Decision tree — contrast checking:
Is it text?
YES → Normal text: 4.5:1 | Large text: 3:1
NO → Is it a UI component or graphic?
YES → 3:1 against adjacent color
NO → Decorative — no requirement
4. Semantics
- One
<h1>per page; sequential heading levels (h1 → h2 → h3, never skip) - Landmarks:
<header>,<nav>,<main>,<footer>,<aside> - Multiple
<nav>→ each needsaria-label(e.g. "Main navigation", "Footer links") - Lists use
<ul>/<ol>/<dl>— not styled divs <dialog>for modals (native focus management + backdrop)
5. Dialog / Modal a11y (recipe)
<!-- Native dialog — best practice for modals -->
<dialog id="my-dialog" aria-labelledby="dialog-title">
<h2 id="dialog-title">Confirm action</h2>
<p>Are you sure you want to proceed?</p>
<form method="dialog">
<button value="cancel">Cancel</button>
<button value="confirm" autofocus>Confirm</button>
</form>
</dialog>
<script>
// Open
document.getElementById('my-dialog').showModal();
// Native dialog automatically traps focus + adds backdrop + handles Escape
</script>
6. Color-blind safe patterns
| State | Don't (color only) | Do (color + indicator) |
|---|---|---|
| Error | Red border | Red border + error icon + text |
| Success | Green text | Green text + checkmark icon |
| Required | Red asterisk only | Asterisk + "(required)" text |
| Active tab | Blue color | Blue + underline/weight + aria-selected |
| Disabled | Gray color | Gray + disabled attr + reduced opacity |
7. Touch & tap targets
- Interactive elements ≥ 44×44px on mobile (WCAG 2.5.8 target size)
- ≥ 40×40px acceptable on dense desktop UIs
- No overlapping tap targets
- Adequate spacing between adjacent interactive elements (≥ 8px gap)
Quick audit method
Run in this order (5-minute check):
- Tab through — can you reach everything? Is focus visible?
- Screen reader — does heading structure make sense? Are buttons/links announced correctly?
- Contrast check — run browser DevTools contrast checker or axe
- Zoom to 200% — does layout still work?
- Color only — remove color perception (Chrome DevTools → Rendering → Emulate vision deficiency)
Checklist
- Keyboard path works without mouse
- Focus visible everywhere interactive
- Focus trapped in modals, restored on close
- Skip link present when nav is long
- Labels / aria-label present on all interactive elements
- Live regions for dynamic content updates
- Contrast meets AA (4.5:1 text, 3:1 large/UI)
- No color-only state communication
- Semantic structure (one h1, sequential levels, landmarks)
- Touch targets ≥ 44px on mobile
- Dialogs use native
<dialog>or proper focus management
Depth
Full checklist: references/accessibility-checklist.md.
Contrast & color-alone rules in context: references/ux-foundations.md.