Use when reviewing UI for accessibility — WCAG 2.2 AA, keyboard nav, focus, ARIA, contrast, screen-reader semantics — even on 'is this a11y-OK?' or 'mach das barrierefrei'.
Scanned 9/2/2026
Install to Claude Code
npx -y skills add event4u-app/agent-config --skill accessibility-auditor --agent claude-codeInstalls into .claude/skills of the current project.
Are you the author of Accessibility Auditor?
Add the live security badge to your README — it updates automatically with every re-scan.
[](https://www.skillsdirectory.com/skills/event4u-app-accessibility-auditor-agent-config)More formats (shields.io, HTML) on the badges page.
---
model_tier: medium
name: accessibility-auditor
description: "Use when reviewing UI for accessibility — WCAG 2.2 AA, keyboard nav, focus, ARIA, contrast, screen-reader semantics — even on 'is this a11y-OK?' or 'mach das barrierefrei'."
personas:
- frontend-engineer
domain: quality
workspaces:
- engineering
packs:
- engineering-base
token_budget_class: rich
---
# accessibility-auditor
> Audit a UI surface against WCAG 2.2 AA, keyboard-only operation,
> and screen-reader semantics. Output is a verdict with cited
> failures, not a vibes check. Pair with
> [`tailwind-engineer`](../tailwind-engineer/SKILL.md) for token-level
> contrast fixes and [`ui-component-architect`](../ui-component-architect/SKILL.md)
> for structural fixes (landmarks, heading order).
## When to use
- A new screen, component, or form lands and a11y has not been
reviewed yet.
- A bug report mentions keyboard, screen reader, focus order,
contrast, or "user can't reach the X button".
- A modal, dropdown, popover, tab strip, or tree view is being
introduced — these are the highest-yield bug zones.
- German triggers: "barrierefrei prüfen", "Tastatur-Bedienung",
"Screenreader testen".
Do NOT use when:
- The visual design itself is the question (palette, type scale) —
route to [`fe-design`](../fe-design/SKILL.md).
- The diff has no UI surface — accessibility audits without a UI are
speculation.
- A specific component spec is missing entirely — get the component
built first via the stack-specific skill, then audit.
## Procedure
### 1. Identify the interaction surfaces
List every interactive element on the screen: links, buttons,
inputs, custom widgets (combobox, tab, dialog, tree). Each row
gets a verdict in step 5; missing one is a coverage failure.
### 2. Walk the four checklists
**Perceivable** — text alternatives for non-text (`alt`, `aria-label`),
contrast ≥ 4.5:1 for body / 3:1 for large or UI components, no
colour-only state ("error in red" must also be iconic or text).
Grounded reference for palette + chart verdicts: the adopted corpus's
WCAG-adjusted token sets (`--domain color`), chart a11y grades +
colorblind fallbacks (`--domain chart`), and mobile touch/a11y rules
(`--domain web`, 44pt targets) via
[`design-intelligence`](../design-intelligence/SKILL.md) — cite the
corpus row; the WCAG 2.2 AA *method* stays this skill's own checklists.
Widget-pattern selection (dialog, combobox, tabs, toast, table, form
errors, drag-reorder …) grounds in the ARIA-APG corpus:
`./scripts-run <skills-root>/corpus-grounding/scripts/ground search
--manifest <skills-root>/accessibility-auditor/data/manifest.json
"<component>"` →
pattern, implementation, WCAG refs, anti-patterns
([`data/aria-patterns.csv`](data/aria-patterns.csv)).
**Operable** — every interactive element reachable by `Tab`, focus
order matches visual order, focus indicator visible (≥ 3:1 against
adjacent), `Esc` closes overlays, no keyboard traps.
**Understandable** — labels associated (`label[for]` or wrapping),
errors named in text (not just border colour), language attribute
on `<html>`, predictable navigation across pages.
**Robust** — landmarks present (`<header>`, `<nav>`, `<main>`,
`<footer>`), heading order without skips, ARIA only when no native
element exists, custom widgets follow ARIA-APG patterns.
**Reduced motion — audit the PRESENTATION, not the presence of the
query.** A `prefers-reduced-motion` block is easy to grep and proves
nothing: `animation: none` on a scroll-revealed section leaves the
content invisible, and a scrubbed sequence with its motion removed
leaves the story untold. So the finding is about what the surface
shows **instead**:
| Motion carries | Reduced-motion presentation | Failure |
|---|---|---|
| A reveal (content fades/slides in) | The content at its **final** state, visible from the start | `opacity: 0` left standing — the content never appears |
| A scrubbed sequence (scroll drives progress) | Each beat at its **resting** state, reachable by ordinary scrolling | Motion disabled and the beats collapse onto each other |
| A transition between states | An instant state change, still announced | The state changes with no perceivable feedback |
| Decoration (parallax, drift, ambient) | Nothing — removal is the correct presentation | — |
Decoration is the only case where removal is the answer, which is why
`animation: none` reads as correct so often and is wrong so often. Ask
per block: *if the motion never plays, is the content still there and
still understandable?* Record the answer, not the query.
### 3. Run the keyboard pass
`Tab` from page start: every interactive element receives focus,
in visual order, with a visible indicator. `Shift-Tab` reverses
cleanly. `Enter` / `Space` activate per role. Arrow keys work in
composite widgets (tab strip, listbox, menu). `Esc` dismisses the
top-most overlay only.
### 4. Run the screen-reader pass
Use VoiceOver (macOS), NVDA (Windows), or `aria-live` log
inspection. Each surface announces: role, name, state, value,
description (in that order). State changes (loading, expanded,
selected, error) announce. Decorative images are silent.
### 5. Score and report
For each surface from step 1:
| Surface | WCAG SC | Pass / Fail | Evidence |
|---|---|---|---|
| Submit button | 1.4.3 | Fail | contrast 3.1:1, expected 4.5:1 |
| Modal | 2.1.2 | Fail | Tab leaves modal — focus trap missing |
Verdict at the bottom: **AA-pass** (zero fails), **AA-pass-with-risk**
(non-blocking gaps + plan), or **AA-fail** (blocking — fix before
ship).
## Output format
```
Scope: <screen / component / route>
Surfaces: <count from step 1>
Tools used: <axe-core | manual | VoiceOver | NVDA — pick at least 2>
Findings:
1. <SC> <Pass/Fail> <Evidence + file:line if known>
2. ...
Verdict: <AA-pass | AA-pass-with-risk | AA-fail>
Top 3 fixes: <ordered by user impact>
```
## Same-ramp contrast + dark-mode self-test (procedures, not a palette)
Two deterministic procedures — they reference the consumer's tokens / an
upstream color ramp, they never vendor a hex table:
- **Same-ramp contrast.** Text on a colored fill uses a **darker stop of the
same color family**, never plain black/gray dropped on top — same-ramp keeps
the contrast on-brand and predictable. Title and subtitle are **two different
stops** of that ramp (hierarchy from the ramp, not from an off-ramp gray).
- **Mandatory dark-mode self-test.** Before finalizing, ask: *"if the background
were near-black, would every text element still meet 4.5:1 (body) / 3:1
(large/UI)?"* A ramp that only works on light is a fail. Run the test even
when the brief is light-only — dark mode arrives later.
The ramp itself is authoritative in [`design-tokens`](../design-tokens/SKILL.md)
(token derivation) and the consumer's [`brand-consistency`](../../rules/brand-consistency.md)
tokens — this procedure sits beside that authority, it does not duplicate the
values.
## Gotcha
- `aria-label` on a `<button>` overrides its text content for
screen readers — only use when the visible text is not
descriptive (icon-only buttons).
- `tabindex="-1"` removes from tab order *and* allows programmatic
focus; `tabindex="0"` adds to tab order. `tabindex >= 1` is
almost always wrong — fix the source order.
- Native `<button>` and `<a>` come with role + keyboard for free;
reaching for `role="button"` on a `<div>` is a regression.
- Contrast on disabled state has no WCAG threshold — but if the
user cannot tell it is disabled, that is a 1.4.1 failure on
state cue.
## Do NOT
- Do NOT declare AA-pass without a keyboard-only pass; an axe-core
green is necessary, not sufficient.
- Do NOT add ARIA "to be safe" — wrong ARIA is worse than no ARIA.
Native semantics first.
- Do NOT silence the audit on "internal-only tool" or "small user
base"; legal exposure does not scale with audience size in most
jurisdictions.
- Do NOT close a finding by adjusting the test instead of the UI;
the user's experience is the ground truth, not the test report.
## Why this skill is rich
WCAG 2.2 AA compliance is non-negotiable detail — each success criterion
has a specific testable condition, failure mode, and remediation path.
The skill carries the full WCAG criterion matrix with concrete test
procedures (not just "check contrast"), keyboard-navigation patterns
(roving tabindex, trap management, escape-key handling), ARIA role/
property/state tables, and screen-reader-specific edge cases per
assistive technology. Condensing to "check contrast and add aria-labels"
loses the test procedures that turn a vague finding into a reproducible
failure with a specific remedy. Accessibility bugs that are described
vaguely cannot be verified as fixed.
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!