| name | auditing-web-accessibility |
| description | Use when auditing or debugging web UI accessibility — running a WCAG audit or a11y sweep of a site, page, or branch diff, OR interactively diagnosing a reported accessibility issue: a screen-reader / VoiceOver, keyboard, focus, focus-trap, contrast, ARIA, form-error, live-region, or modal problem in a rendered web app, or a failing axe / a11y check. Covers DOM-first audits and inline component debugging on macOS with VoiceOver. |
| version | 0.1.0 |
Auditing web accessibility
DOM-first, source-traced, scope-honest. Inspect the rendered page with tools that actually
run, report every tool-confirmed finding, and attach a source location + fix to each. The model
already knows WCAG; the discipline this skill enforces is running the toolchain and reporting
only what it confirms — never a confident verdict reasoned from static source.
Target conformance: WCAG 2.2 Level AA (A/AA only; AAA out of scope). Local CLI tool, macOS +
VoiceOver. No MCP, no CI gate.
Three rules that override the urge to "just review the code"
- No tool, no verdict. If preflight stops (missing deps, browser binaries, VoiceOver
permission, or auth), surface its instructions and HALT. Do not fall back to reading
the HTML and reasoning about what axe "would" catch. A confident report produced from a
non-running toolchain is exactly the failure mode this skill exists to prevent.
- Evidence is what a tool reported — axe output, the real Tab-loop capture, guidepup's
spoken phrases. Every finding is DOM-confirmed and reproducible. Never down-grade a finding
because a tool "didn't run" — run the tool.
- Report every confirmed finding. Source-tracing routes the fix and dedups; it is not a
precondition for reporting. A finding axe and guidepup agree on is high-confidence with zero
source match. Runtime-injected, ungreppable findings are often the highest severity.
The two modes
| Mode | Entry | Scope | Output | Tier-2 (guidepup) |
|---|
| audit | web-a11y-auditor agent (dispatched by the main thread) | whole sitemap (opens with a design-system pass), or narrowed to a branch diff | prioritized severity-grouped report | Selective — DS components + critical flows + axe-flagged widgets; full coverage on a small diff |
| debug | inline skill, in the main conversation | one component / a reported issue / a flow | root cause + proposed fix (apply only after confirmation); optionally authored Playwright specs (commit only with --commit or confirmation) | Full on the target |
audit absorbs full-audit and diff-review (a diff just narrows scope — same pipeline, same
report). debug absorbs debug and write-tests. With no renderable web UI (no reachable dev
server, no templates/components), exit early with that finding — do not fabricate an audit.
Pipeline — run in order
Run preflight from ${CLAUDE_PLUGIN_ROOT}/assets/harness/SETUP.md. If it stops, surface its
instructions and halt (rule 1). Then:
- Scope. Resolve mode + target. For
audit, the main thread dispatches the
web-a11y-auditor agent (a skill body cannot spawn an agent). For debug, continue inline.
- Authenticate. Generate
storageState via the setup project; assert the logged-in marker so
an expired session fails loudly instead of auditing the login page. Treat authenticated
artifacts as sensitive — keep them out of commits; require opt-in for production data.
- Discover routes (cascade, stop at first that yields routes; always report which source +
the count + any
--max-routes ceiling): explicit list / a11y.routes.json → sitemap.xml /
robots.txt → bounded same-origin crawl from the authenticated home page. Crawl is best-effort
(misses POST-only / JS-built / deep-linked routes); parameterized routes (/orders/:id) are
sampled, not enumerated — say so. Diff-scoped audits start from the changed-template→route
map; on a base/layout/CSS/token change or any uncertain mapping, audit the full sitemap and
say so — never silently under-scope.
- Run validation tools (tiered) — see
references/tooling.md.
- Tier 1 (always, every mode):
@axe-core/playwright (pinned, tags
wcag2a,wcag2aa,wcag21a,wcag21aa,wcag22aa, exclude best-practice/experimental) + tabbable
- a real
Tab loop (capturing document.activeElement — tabbable alone cannot detect a
non-dismissable trap or tabindex>0 reorder).
- Tier 2 (guidepup / real VoiceOver): applied proportionally by mode (table above).
- Settle before every
analyze() with waitForLoadState('load') + a web-first assertion.
Never networkidle on htmx/SSE apps (an open SSE stream never idles → hang).
- SR assertions target occurrence + tokens (name/role/state) on the normalized utterance,
never the verbatim string. Prefer the a11y tree (
getByRole, toHaveAccessibleName).
- Apply the blind-QA lens — see
references/interaction.md. Unit of review = task flows
(forms, wizards, auth, upload→review→confirm); static pages get a lighter per-page pass. Run
the 6-step Blind QA Trace and let user-impact severity (annoying → workflow-blocking →
trust-breaking → abandonment) modulate the engineering severity.
- Trace findings to source — see
references/tooling.md. Capture a stable handle (accessible
name, nearby text, role, key attrs, DS class/component marker, axe target + html), match to
the producing template/component, attach source_location (file:line). When source can't be
located (runtime-injected ARIA, dynamic state, build transforms, Shadow DOM), label
source: runtime-only and still report it at full severity.
- Emit the artifact — finding schema below, grouped by severity, plus the coverage ceiling
and (audit) the optional last-run diff.
Design system: triage prior, NOT an exemption
Detect the DS name and version (package.json/lockfile + class-prefix fingerprint — here:
@uswds/uswds + usa-*). DS-correct is necessary, not sufficient: every DS component still
passes the full battery; suppress nothing on a DS marker alone. Flag drift (DS class on the
wrong element/semantics). Report a DS-component failure as one systemic finding (one fix, every
instance) — the highest-leverage output. See references/semantics-and-aria.md.
Finding schema (each finding)
- WCAG criterion — number, name, level (against the WCAG 2.2 AA target).
- Source location —
file:line, or source: runtime-only (still reported).
- Evidence — what axe / tabbable / guidepup actually reported (DOM-confirmed; sensitive).
- Blind QA Trace — task frame, non-visual map, action path, change/recovery, friction,
evidence chain (incl. inferred user-impact severity).
- Human QA evidence — when present: task, AT/browser, barrier, impact, defensible WCAG/508 map.
- Confidence — driven by tool agreement + DOM reproducibility (not grep success).
- Recommended fix — in the project's idiom; for DS components, the systemic fix.
- Systemic flag — when one fix resolves many instances.
Plus a summary count, the coverage ceiling, and (diff-scoped) the optional new / fixed /
still-present last-run diff (simple signature: WCAG id + route + selector/html + accessible name).
Coverage ceiling — state in every report
Run/report WCAG 2.2 A/AA only. Surface-but-don't-auto-pass (semantic judgment — for human
review, never a high-confidence pass/fail): 1.1.1 alt quality, 2.4.4 link purpose, 2.4.6
heading descriptiveness. Cognitive / 3.3.x and any semantic-judgment SC are surfaced or
out-of-scope, not verified. A clean or "no findings" report is an engineering signal, not a
WCAG or Section 508 conformance claim. Automated checks + expert review + paid disabled-user QA
on representative tasks is the real operating model; this skill approximates the QA approach,
it does not replace disabled-user testing.
References (load on demand)
references/semantics-and-aria.md — semantic HTML / ARIA / landmarks / alt / headings + the
DS-as-triage-prior (USWDS) details.
references/interaction.md — keyboard / focus / modals / forms / live-regions, the
coverage-beyond-axe success criteria, and the blind-QA task-flow methodology.
references/tooling.md — axe / tabbable / guidepup usage, the assertion strategy, determinism
rules, and the source-tracing mechanism (incl. htmx/Alpine dynamic-DOM handling).