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.
Scanned 9/8/2026
Install to Claude Code
npx -y skills add bg-szy/TOP-SKILLS --skill a11y --agent claude-codeInstalls into .claude/skills of the current project.
Are you the author of A11y?
Add the live security badge to your README — it updates automatically with every re-scan.
[](https://www.skillsdirectory.com/skills/bg-szy-a11y)More formats (shields.io, HTML) on the badges page.
---
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)
1. **Semantics & landmarks** — real `<button>`/`<a>`/`<nav>`/`<main>`; one `<h1>`,
no skipped heading levels; lists are lists. (1.3.1, 2.4.1)
2. **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)
3. **Images & icons** — informative images have meaningful `alt`; decorative ones
have `alt=""`/`aria-hidden`; icon-only buttons have an accessible name. (1.1.1)
4. **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)
5. **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)
6. **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)
7. **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)
8. **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)
9. **Motion & reduced motion** — honor `prefers-reduced-motion`; no autoplay > 5s
without controls; nothing flashes > 3×/sec. (2.3.1, 2.2.2)
10. **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
1. **Scope** — resolve the changed frontend files (or `$ARGUMENTS` globs). If none
match the trigger, say so and stop.
2. **Static pass** — Grep/Read the changed files for the defect signatures in each
dimension above. Record findings with file:line + WCAG criterion + impact.
3. **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.
4. **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
```markdown
# 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).
Is this your skill, or is something wrong with this listing? Request removal or report an issue. Author removals are honored within 72 hours.
No comments yet. Be the first to comment!