Skill in testing
accessibility-audit
Audit UI code or a running page against WCAG 2.2 AA, covering semantics, keyboard access, focus, labels, contrast, ARIA misuse, and motion, using axe-core in a real browser when available and code review when not, with findings ranked by user impact. Use when building or reviewing UI components, before shipping user-facing pages, or when accessibility compliance is required.
Install
As a plugin
Bundled in the testing plugin. Add the marketplace once, then install the bundle; it updates with the marketplace.
/plugin marketplace add KhaledSaeed18/dotclaude/plugin install testing@dotclaudeAs a single item
Run from the project root; the skill lands under .claude/. Copy it to ~/.claude/ to make it global.
npx shadcn@latest add KhaledSaeed18/dotclaude/accessibility-auditFiles installed
.claude/skills/accessibility-audit/SKILL.md
What it does
Audit for the users who hit the wall, not for the checklist: someone on a keyboard who cannot reach the button, a screen-reader user who hears "button button", a low-vision user who cannot read grey-on-white hints. Automated checks catch at most a third of real barriers; the rest come from reading the code and walking the flows. Prefer fixing semantics over adding ARIA; the first rule of ARIA is not to use it when native HTML already does the job.
Step 1: Scope and method
Identify what is being audited (components in a diff, a page, a whole flow) and pick the method:
- Running app available: audit in the browser. Use the
webapp-testingskill's Playwright setup and inject axe-core for the automated pass, then do the manual keyboard walk below. - Code only: review the markup and interaction logic directly. State that contrast and reading-order findings are provisional until seen rendered.
For the automated pass with Playwright:
await page.goto(url);
await page.addScriptTag({ url: "https://cdn.jsdelivr.net/npm/axe-core@4/axe.min.js" });
const results = await page.evaluate(() => axe.run());
// results.violations: id, impact, nodes[].html, nodes[].target
Treat axe output as a floor, not the audit.
Step 2: The manual pass
Work through these in order of user impact:
Keyboard
- Every interactive element reachable with Tab, operable with Enter/Space, in an order that follows the visual flow. No focus traps except intentional ones (modals) that also release on Escape.
- Focus visible at every stop (WCAG 2.2 requires it not be fully obscured). No
outline: nonewithout a replacement. - After actions that move context (opening a modal, deleting a list item, route change in a SPA): focus is placed deliberately, not dropped to
<body>.
Semantics and names
- Native elements for native jobs:
<button>not<div onClick>,<a href>for navigation,<label for>on every form control. A clickable div needs role, tabindex, and key handlers to equal a button; flag it and recommend the element instead. - Every control has an accessible name that matches its visible label; icon-only buttons have
aria-label. Images: informative ones have realalt, decorative ones havealt="". - Headings form an outline (one
h1, no skipped levels used for styling). Landmarks (main,nav) present once each.
ARIA misuse (the most common class of introduced bugs)
- No redundant roles (
role="button"on<button>), no invalid parent/child combos (role="tab"outside a tablist), noaria-hidden="true"on focusable content. - State conveyed both visually and programmatically:
aria-expandedon disclosure triggers,aria-currenton active nav,aria-invalidplus an error message linked witharia-describedbyon failed fields. - Live updates that matter (async errors, toasts, cart counts) announced via a live region; silent visual-only feedback flagged.
Visual
- Text contrast 4.5:1 (3:1 for large text), interactive component boundaries 3:1. Check the greys: placeholder text, disabled-looking-but-enabled buttons, hint text are the usual failures.
- Nothing conveyed by color alone (error = red border only). Content reflows at 320px width / 200% zoom without loss.
- WCAG 2.2 additions: pointer targets at least 24x24 CSS px (or spaced equivalently); no interaction requires dragging without a click alternative.
Motion and time
- Animations respect
prefers-reduced-motion. Auto-advancing carousels/toasts are pausable or long enough to read. No content flashes more than three times per second.
Step 3: Report
Rank findings by user impact: blockers (cannot complete the task at all: unreachable control, trap, unlabeled form) first, then serious (task completable with difficulty), then moderate/polish. For each: the element (selector or file:line), who it fails and how, the WCAG criterion, and the exact fix as code, not advice. Separate "verified in browser" from "found by review, verify rendered". End with the two or three highest-leverage systemic fixes (e.g. "the shared Button component takes an icon without requiring a label prop") rather than only the instance list.
This is the skill as installed, rendered from its source on GitHub. Read it as Markdown.
More in testing
- webapp-testingVerify a web application works in a real browser with Playwright, covering navigation, form submission, interactions, console errors, screenshots, and...
- write-testsGenerate a focused, production-quality test suite for a source file, detecting the project's existing test runner and conventions. Use when a source file...