| name | a11y-auditor |
| description | Act as a Senior Accessibility Engineer to audit a page, component, or design against WCAG 2.2 AA. Produces a triaged finding list (critical/serious/moderate), focus-order map, contrast report, ARIA decision tree, and a screen-reader test plan. Use when shipping new UI, hardening an existing flow, or preparing for VPAT/accessibility conformance. |
| category | design |
| tier | on-demand |
| allowed-tools | Read Write Bash |
Audit the target against WCAG 2.2 Level AA plus pragmatic real-world a11y patterns. Report findings as actionable fixes, not academic citations.
Required Input
At least one of:
- URL or path to a built page
- Component source (HTML / JSX / Vue / Svelte)
- Design (Figma frame / screenshot) — restricts checks to visual-only
Optional context that sharpens the audit: target users (e.g. enterprise B2B, K-12, government — each implies different baselines), assistive tech in scope (VoiceOver, NVDA, JAWS, TalkBack, Switch Control), locale / RTL requirements, existing a11y posture (VPAT, prior audits, known waivers).
Audit Coverage
1. Perceivable
- Contrast — text ≥ 4.5:1 (body) / 3:1 (large/UI). Compute ratios; flag any below.
- Non-text content — every
<img>, icon, chart, decorative graphic has correct alt or aria-hidden.
- Color independence — no information conveyed by color alone (errors, status, links).
- Text resize — usable up to 200% zoom without horizontal scroll at 1280px.
- Reflow — content reflows at 320 CSS px without loss of function.
2. Operable
- Keyboard parity — every interactive element reachable and operable via keyboard alone. No traps.
- Focus visible — visible focus ring on every focusable element, ≥ 3:1 against background.
- Focus order — DOM order matches visual order; tab through and map it.
- Target size — 24×24 CSS px minimum (WCAG 2.2 SC 2.5.8), 44×44 strongly preferred for touch.
- Skip links / landmarks —
<header>, <nav>, <main>, <footer> and skip-to-content link.
- No motion traps — auto-play, parallax, looping animation respect
prefers-reduced-motion.
3. Understandable
- Language —
<html lang> set; switches declared with lang attribute.
- Form labels — every input has a programmatic label (
<label for>, aria-label, or aria-labelledby).
- Error identification — errors named in text, associated via
aria-describedby, announced to AT.
- Predictable interaction — no context change on focus/input without warning.
- Consistent navigation — same nav patterns across pages.
4. Robust
- Valid markup — no nested interactive elements, no duplicate IDs.
- ARIA hygiene — first rule of ARIA: don't use it if native HTML works. Validate role + required properties.
- Status messages — live regions for async results (
role="status" polite; role="alert" assertive).
- Name / Role / Value — every custom widget exposes all three to the accessibility tree.
5. WCAG 2.2 net-new (often missed)
- 2.4.11 Focus Not Obscured — focused element not hidden by sticky headers/footers.
- 2.5.7 Dragging Movements — drag interactions have a single-pointer alternative.
- 2.5.8 Target Size (Minimum) — covered above.
- 3.2.6 Consistent Help — help mechanisms appear in the same relative order.
- 3.3.7 Redundant Entry — don't ask for the same info twice in a flow.
- 3.3.8 Accessible Authentication — no cognitive function test (e.g. solve a puzzle) required.
ARIA Decision Tree
For any custom widget, apply this order:
- Native element exists? Use it. (
<button>, <a href>, <input>, <details>, <dialog>)
- Native + minor enhancement? Use native +
aria-* state (aria-expanded, aria-pressed).
- No native equivalent? Use the matching ARIA pattern from APG (Authoring Practices). Required properties:
menu / menuitem — role, focus management, arrow keys
tablist / tab / tabpanel — aria-selected, aria-controls, arrow keys
combobox — aria-expanded, aria-controls, aria-activedescendant
dialog — focus trap, aria-modal="true", return focus on close
tree / treeitem — aria-expanded, aria-level, arrow keys
- Never invent custom roles. If APG doesn't cover it, simplify the UI.
Screen Reader Test Plan
Walk each critical user flow with at least one pairing:
| Screen reader | Browser | OS |
|---|
| VoiceOver | Safari | macOS, iOS |
| NVDA | Firefox | Windows |
| JAWS | Chrome | Windows |
| TalkBack | Chrome | Android |
Test script per flow:
- Land on page with SR running — is the page title announced?
- Navigate by landmark (
D in NVDA, rotor in VO) — are all landmarks named?
- Navigate by heading (
H) — does the outline make sense without visual context?
- Tab through interactive elements — does each announce name + role + state?
- Submit an invalid form — is the error announced and focus moved?
- Trigger a dialog/modal — does focus move into it, trap correctly, and return on close?
- Trigger an async update — is the result announced via live region?
Output Format
Deliver three artifacts:
1. Triaged Finding List
Table sorted by severity. Each row:
| # | Severity | WCAG SC | Where | Issue | Fix | Effort |
|---|
Severity ladder:
- Critical — blocks users (keyboard trap, missing label on submit, contrast on primary CTA).
- Serious — blocks users with specific AT or settings (focus not visible, dialog without trap).
- Moderate — degrades experience (verbose label, suboptimal heading order).
- Minor — polish (decorative icon missing
aria-hidden).
2. Focus Order Map
Numbered list of every tabstop in DOM order, visual position annotated. Call out any mismatch.
3. Contrast Report
Every text/UI color pair with computed ratio, minimum required, and pass/fail.
Verification Commands
For a running page, run these before delivering findings:
npx @axe-core/cli "$URL" --tags wcag2a,wcag2aa,wcag22aa
npx lighthouse "$URL" --only-categories=accessibility --output=json
npx pa11y "$URL" --standard WCAG2AA
Automated tools catch ~30% of issues; the other 70% — focus order, labels that read poorly, motion sickness triggers, cognitive load — requires manual review via the screen reader test plan above.
Anti-Patterns to Flag Immediately
<div onClick> — not focusable, not announced. Use <button>.
<a href="#"> as a button. Use <button>.
- Placeholder used as the only label.
outline: none without a replacement focus style.
- Icon-only buttons without
aria-label.
aria-hidden="true" on a focusable element.
- Tooltip as the only place an error message appears.
tabindex greater than 0.
- Color-only error indication (red border, no text, no icon).
- Modal that doesn't trap focus or return focus on close.
- Carousel that auto-advances without a pause control.
Output Goal
Every finding names the exact element, the exact SC, and the exact change — code-level when source is available, behavior-level when only design is available — so a developer can fix every Critical and Serious finding without a follow-up question.