From harness
Run axe-core against changed routes and gate on WCAG 2.1 AA violations; invoked by the pipeline after frontend Build and by design-qc in-process during Final Gate.
How this skill is triggered — by the user, by Claude, or both
Slash command
/harness:accessibility-checkqa-engineerThe summary Claude sees in its skill listing — used to decide when to auto-load this skill
Scans changed frontend routes with axe-core against WCAG 2.1 A/AA rules. Gates the pipeline on any gating violation (wcag2a, wcag2aa, wcag21a, wcag21aa tagged). Reports per-route results so engineers can pinpoint which route introduced the violation.
Scans changed frontend routes with axe-core against WCAG 2.1 A/AA rules. Gates the pipeline on any gating violation (wcag2a, wcag2aa, wcag21a, wcag21aa tagged). Reports per-route results so engineers can pinpoint which route introduced the violation.
Invoked in two modes:
.tsx, .jsx, .vue, .svelte, or CSS files. Parallel with /harness:design-qc.skills/design-qc/SKILL.md Step 6.26 while the design-qc dev server is already running.git diff --name-only origin/main...HEAD
Supported router layouts:
app/ or pages/ — derive URL path from file path
app/dashboard/page.tsx → /dashboardpages/dashboard.tsx → /dashboardsrc/pages/ — derive URL path from file path/ unconditionallyRoot is always included regardless of what the diff shows.
If the resulting set is {'/'} (zero non-root routes detected from the diff), that is valid — scan / only. Never produce a zero-length URL list.
node hooks/_lib/axe_runner.js --url / --url /dashboard [--url ...]
Repeated --url flags, one per route.
The pipeline orchestrator starts the dev server per the project ## Dev Server contract before invoking this skill. If the ## Dev Server contract is absent from the project CLAUDE.md:
A11Y_CHECK_SKIPPED with skip_reason: no-dev-server-contractAfter route detection (Step 1), invoke:
node hooks/_lib/axe_runner.js --url <url1> [--url <url2>...]
Parse the exit code and stdout JSON (output is stdout-only).
When called from design-qc Step 6.26, the dev server is already running. design-qc passes the axeRunFn callable (bound to the Playwright page). Call run_main(argv, { axeRunFn }) directly from Node.js rather than spawning a child process.
The stdout JSON shape:
{
"verdict": "A11Y_CHECK_PASSED | A11Y_CHECK_FAILED | A11Y_CHECK_SKIPPED",
"gating_violations": [
{
"id": "color-contrast",
"help": "Elements must have sufficient color contrast",
"nodes": [{ "target": ".btn", "html": "<button>" }],
"route_url": "/dashboard"
}
],
"incomplete": [],
"routes": [
{
"url": "/",
"verdict": "A11Y_CHECK_PASSED",
"gating_violations": [],
"incomplete": []
}
],
"skip_reason": "env-hatch | no-dev-server-contract | browser-launch-failed"
}
Exit code mapping:
0 → A11Y_CHECK_PASSED1 → A11Y_CHECK_FAILED2 → A11Y_CHECK_SKIPPEDA11Y_CHECK_PASSED — all routes clean; pipeline continuesA11Y_CHECK_FAILED — halt; list gating violations with {id, help, nodes} actionabilityA11Y_CHECK_SKIPPED — infrastructure unavailable; pipeline continues; emit scratchpad warning| Verdict | Polarity | Meaning |
|---|---|---|
A11Y_CHECK_PASSED | success | All scanned routes have zero WCAG 2.1 A/AA gating violations |
A11Y_CHECK_FAILED | failure | One or more routes have gating violations; lists each with id, help, nodes, route_url |
A11Y_CHECK_SKIPPED | info | Infrastructure unavailable (no dev server, browser launch failure, or env hatch); reason ∈ {no-dev-server-contract, browser-launch-failed, env-hatch} |
GATING_TAGS: ['wcag2a', 'wcag2aa', 'wcag21a', 'wcag21aa']. A violation is gating iff its tags array intersects GATING_TAGS. best-practice, experimental, cat.* tags are never gating.
axe incomplete results: reported in the incomplete[] array; they never gate. They are advisory — engineers should review them but they do not block the pipeline.
Multi-route aggregation rule: every scanned route must be clean for A11Y_CHECK_PASSED. One route with gating violations → A11Y_CHECK_FAILED; per-route result blocks are present in routes[] for all scanned URLs.
| Failure | Condition | Outcome |
|---|---|---|
| No dev server contract | ## Dev Server absent from project CLAUDE.md | A11Y_CHECK_SKIPPED with skip_reason: no-dev-server-contract; scratchpad warning with token axe-scan-failed |
| Browser launch failed | axeRunFn throws | A11Y_CHECK_SKIPPED with skip_reason: browser-launch-failed; scratchpad warning with token axe-scan-failed |
| Env hatch | CLAUDE_A11Y=0 set | A11Y_CHECK_SKIPPED with skip_reason: env-hatch; no scratchpad warning |
| WCAG violations found | gating violations > 0 | A11Y_CHECK_FAILED; list violations with actionability fields |
Scratchpad warning format for skip conditions (except env-hatch):
---
category: warning
---
axe-scan-failed: <skip_reason>. Route(s) not scanned: <urls>.
When axe_runner.js is invoked directly (node hooks/_lib/axe_runner.js --url ...), it resolves playwright-core and axe-core in this order:
<process.cwd()>/node_modules/ — the consumer project's installed packages.tests/fixtures/accessibility-check/.deps/node_modules/ — the fixture dependency cache (populated by the fixture integration test).If neither location provides both playwright/playwright-core and axe-core, the runner emits A11Y_CHECK_SKIPPED with skip_reason: browser-launch-failed and exits 2. This is the legitimate env-unavailability path, not a hard failure.
The resolver is DI-testable: _resolve_cli_deps(candidates, requireFn, readFileFn) accepts injectable require and readFile functions.
CLAUDE_A11Y=0 — skip all axe scanning; emit A11Y_CHECK_SKIPPED with skip_reason: env-hatch. Use when the Playwright environment is unavailable and the operator accepts the skip.
Verdict: A11Y_CHECK_PASSED / A11Y_CHECK_FAILED / A11Y_CHECK_SKIPPED
Routes scanned: [list]
Gating violations: [list with id, help, route_url, nodes]
Incomplete (advisory): [count]
npx claudepluginhub paulingham/.claude --plugin harnessProvides UI/UX resources: 50+ styles, color palettes, font pairings, guidelines, charts for web/mobile across React, Next.js, Vue, Svelte, Tailwind, React Native, Flutter. Aids planning, building, reviewing interfaces.
Fetches up-to-date documentation from Context7 for libraries and frameworks like React, Next.js, Prisma. Use for setup questions, API references, and code examples.