| name | a11y |
| version | 1.0.0 |
| description | Accessibility audit for frontend changes — WCAG 2.2 AA, screen-reader
compatibility, keyboard navigation, focus management, ARIA correctness, color
contrast, and reduced-motion. Static diff-scoped analysis (safe to auto-run) plus
an optional dynamic axe pass against a running app. Use when asked to "check
accessibility", "a11y audit", "screen reader check", "WCAG", "is this
accessible", "keyboard navigation", "ARIA review", or after heavy frontend work.
|
| triggers | ["a11y","accessibility audit","screen reader check","wcag","is this accessible","keyboard navigation check","aria review"] |
| argument-hint | <optional: paths/globs to scope, or a dev URL for the dynamic pass> |
| allowed-tools | ["Bash","Read","Grep","Glob","Task","WebFetch"] |
/a11y
Audit frontend changes for accessibility. Two layers:
- Static (safe to auto-run, diff-only): read the changed JSX/TSX/Vue/Svelte/
HTML/CSS and flag accessibility defects from the markup itself — no browser, no
external tools.
- Dynamic (recommend / opt-in): when a dev URL is reachable, run an
axe-core / Chrome DevTools MCP pass for computed contrast, focus order, and the
screen-reader accessibility tree — things you can't see from source alone.
Hard rules
- Ground every finding in evidence: file:line, the offending element, the WCAG
success criterion (e.g. 1.1.1, 2.4.3, 4.1.2), and the concrete user impact
("screen-reader users hear an unlabeled button", "keyboard users can't reach X").
- Severity by user impact, not by rule pedantry. A control no screen-reader or
keyboard user can operate is CRITICAL; a missing
lang attribute is LOW.
- Smallest correct fix. Prefer native semantics (
<button>, <label>, <nav>)
over ARIA bolted onto a <div>. The first rule of ARIA: don't use ARIA if a
native element does the job.
- Scope to the diff. Audit changed components, not the whole app.
When this is worth running (the trigger)
Run on heavy frontend changes likely to affect assistive tech, not every style
tweak. Trigger when the diff includes any of:
- new/changed interactive components (buttons, links, menus, tabs, dialogs,
tooltips, accordions, comboboxes, drag-drop)
- forms (inputs, labels, validation, error messaging)
- images / media / icons (alt text, decorative vs informative, captions)
- ARIA attributes,
role=, tabindex, or aria-* props
- focus / keyboard handling (
onKeyDown, focus(), focus traps, route changes,
modal open/close)
- color / contrast changes in styles, or conveying state by color alone
- motion / animation (autoplay, transitions, parallax)
A pure copy/spacing CSS change with none of the above does not need /a11y.
What to check (WCAG 2.2 AA, screen-reader first)
- Semantics & landmarks — real
<button>/<a>/<nav>/<main>; one <h1>,
no skipped heading levels; lists are lists. (1.3.1, 2.4.1)
- Name, role, value — every control has an accessible name (visible
<label>,
aria-label, or text); custom widgets expose correct role + state. Flag
<div onClick> with no role/tabindex/keyboard handler. (4.1.2)
- Images & icons — informative images have meaningful
alt; decorative ones
have alt=""/aria-hidden; icon-only buttons have an accessible name. (1.1.1)
- Forms — every input has an associated
<label for>/aria-labelledby; errors
are programmatically tied (aria-describedby) and announced; autocomplete
where relevant. (1.3.1, 3.3.1, 3.3.2)
- Keyboard — all interactive elements reachable and operable by keyboard;
visible focus indicator; logical focus order; no keyboard traps; skip link for
repeated nav. (2.1.1, 2.4.3, 2.4.7, 2.1.2)
- Focus management — on modal/dialog open, focus moves in and is trapped; on
close, focus returns to the trigger; on SPA route change, focus/announce the new
view. (2.4.3)
- Screen-reader announcements — dynamic updates use
aria-live/role=status;
visually-hidden text (sr-only) where context is visual-only;
aria-hidden never on focusable content. (4.1.3)
- Color & contrast — text ≥ 4.5:1 (≥ 3:1 large), UI/graphics ≥ 3:1; never
convey meaning by color alone (pair with icon/text). (1.4.3, 1.4.11, 1.4.1)
- Motion & reduced motion — honor
prefers-reduced-motion; no autoplay > 5s
without controls; nothing flashes > 3×/sec. (2.3.1, 2.2.2)
- Reflow & zoom — usable at 200% zoom / 320px without loss of content; no
user-scalable=no; touch targets ≥ 24×24px. (1.4.10, 1.4.4, 2.5.8)
Steps
- Scope — resolve the changed frontend files (or
$ARGUMENTS globs). If none
match the trigger, say so and stop.
- Static pass — Grep/Read the changed files for the defect signatures in each
dimension above. Record findings with file:line + WCAG criterion + impact.
- Dynamic pass (if a dev URL is given or in
CLAUDE.md/package.json) — load
the affected routes and run axe-core via the chrome-devtools MCP (or a project
axe runner). Report computed contrast failures, focus-order issues, and
accessibility-tree gaps the static pass can't see. If no URL is reachable, record
dynamic pass SKIPPED (no dev server) — don't silently drop it.
- Report — group by severity; give the smallest fix per finding (prefer native
semantics). Draft the corrected markup as a code block; do not apply it.
Report format
# Accessibility audit — <scope> @ <date>
## CRITICAL (blocks assistive-tech users)
- <file:line> — <element> — WCAG <criterion> — <impact> — <fix>
## SERIOUS / MODERATE / MINOR
- …
## Dynamic pass
- axe: N violations (or: SKIPPED — no dev server)
Anti-patterns (do not do)
- Adding ARIA to "fix" something a native element already does correctly.
- Flagging style-only diffs with no interactive/semantic/contrast change.
- Reporting "low contrast" without the measured ratio (use the dynamic pass).
- Applying fixes — this audits and drafts; the user (or
/design-review) applies.
Related commands
/design-audit — broader visual/UX audit; /a11y is the deeper accessibility
layer. Run them together on UI changes.
/web-perf — the performance counterpart for frontend changes.
/design-review — fixes and commits visual + a11y issues this audit finds.
/qa-only — functional browser QA; pairs with /a11y (works + accessible).