review-a11y — accessibility review of changed code
The review counterpart of the ultra11y skill: where ultra11y produces full audits and
compliance deliverables, this skill reviews exactly the code under change — a diff, a
branch, a PR — and reports like a code reviewer. Same division of labour: the
deterministic, install-free engine bundled with this skill does the mechanical work
(machine-checkable WCAG 2.2 AA non-conformities, each tied to a success criterion, cited
file:line), and the AI agent adjudicates the judgment — refute false positives, decide
alt relevance/link purpose/focus logic statically from the code, and route what needs a rendered
DOM to scan. A human is at most optional oversight.
Engine resolution: every command below is node scripts/ultra11y.mjs … where
scripts/ultra11y.mjs lives next to this SKILL.md (the engine ships inside the skill;
no install, no key). Run it with the reviewed project as the working directory. In the
ultra11y repository itself, the repo root's scripts/ultra11y.mjs is the same file.
On Claude Code that path is ${CLAUDE_SKILL_DIR}/scripts/ultra11y.mjs; on Codex and
OpenCode it is the skill directory the plugin installed. If neither resolves — a harness
with no skill system — npx -y ultra11y <command> is the same engine. Per-harness deltas:
references/harnesses.md in the ultra11y skill.
Core rules (shared with ultra11y):
- Never invent a non-conformity: report only what the engine found or what you can
see and cite in the code under review.
- Review the change, not the world: findings outside the reviewed scope are noted in
one line at most ("pre-existing, out of scope"), never mixed into the verdict.
- Residual is explicit, never silently conforming: the rendering criteria (contrast,
focus visibility, zoom/reflow, content-on-hover) are named as residual risks for the changed
components — never asserted from source, resolved in the rendered page (
scan). The
judgment criteria (alt relevance, link purpose, reading order) the AI agent adjudicates
from the code itself, recording a reason — no status without a justified verdict.
- Language: write the review in the conversation's language, but technical tokens
stay in English even in French prose —
aria-live stays aria-live (never
« région live »), same for tabindex, alt, role="alert", landmark role names.
1. Scope — review what changed
Pick the FIRST that applies (ask only when genuinely ambiguous):
node scripts/ultra11y.mjs audit <files the user named> --jsx --json # explicit files
node scripts/ultra11y.mjs audit --staged --graph --json # staged snapshot (pre-commit review)
node scripts/ultra11y.mjs audit --changed --graph --json # working diff
node scripts/ultra11y.mjs audit --since origin/main --graph --json # the whole branch (PR review) — use the repo's default branch
- Always pass
--graph for JSX/TSX (real AST + cross-file rules: icon-only component used
without a name, anchor target defined in another file…); it also suppresses a class of
single-file false positives.
- Pass
--lang matching the conversation so the engine's message/remediation strings are
in the review's language (unset, the CLI auto-detects — a fallback, not a substitute).
- Whole-repo review is an audit, not a review — offer the
ultra11y skill instead.
- Test/story markup (
*.test.*, *.stories.*, __tests__/) is excluded by default; name
such a file explicitly to include it, or pass --no-default-excludes.
2. Judge — the engine's findings are candidates, not verdicts
For each finding (--json gives criteria, severity, file:line, message,
remediation):
- Confirm the occurrence: open the cited line; the offending element must really be
there in the change under review.
preliminary: true findings (.vue/.svelte/.astro source, library components):
the template parser cannot see slots, conditionals or provide/inject wiring. Confirm
against the rendered DOM if captures exist (.ultra11y/captures, from the ultra11y
skill's render --setup harvester), otherwise report them as provisional with that
caveat — or refute them with evidence from the code.
- Library-rendered JSX (DSFR, MUI…): a clean source review can still ship broken HTML
(false negatives) — say so explicitly when the change touches such components, and
recommend a rendered-DOM check (
scan, or the ultra11y capture pipeline).
- The judgment criteria are the AI agent's to adjudicate: alt text relevance, link purpose
in context, visible label vs accessible name, keyboard/focus logic of the changed component
(read the full component source, not just the flagged line), recording a reason for each call.
- Refuting a finding requires cited evidence (the code that disproves it); silently
dropping findings is forbidden.
3. Report — a review, not a compliance document
Render in the conversation's language (Core rule 4), grouped by severity:
## Accessibility review — <scope, e.g. branch feat/x vs main, 12 files>
### 🔴 Blocking
- `src/components/Form.tsx:42` — **3.3.2 Labels or Instructions** — input has no
label and placeholder is its only hint → associate a real <label for>, keep the
placeholder as an example only.
### 🟠 Major / 🟡 Minor
- …same shape…
### Residual risks (rendering-dependent — resolve in the rendered page via scan)
- contrast of the new button variants (1.4.3), focus visibility on the custom menu (2.4.7)
### Verdict
1 blocking, 2 major to fix before merge; 2 provisional SFC findings to confirm on the
rendered DOM; residual risks listed above.
- Keep the engine's
remediation text as the base of each fix — it is already localized
and criterion-grounded.
- Pre-existing issues spotted in passing go in ONE final line, out of the verdict.
Running as a subagent. When the user explicitly requests this review in a subagent,
return the complete report above to the caller. A hook or another skill may suggest
this review; wait for the user's explicit invocation before loading it.
4. After the review (opt-in)
- Safe mechanical fixes:
fix --staged --write --safe (auto-applies the safe codemods
to staged files and re-stages them) or node scripts/ultra11y.mjs fix <files> --write;
dry-run without --write.
- Formal deliverables (dated report, auditor conformance blocks, PRD backlog, GitHub
issues, RGAA/country-standard vocabulary): that is the
ultra11y skill — same engine,
same findings (report, prd, check/verify gates).
- Mechanical gate: the Claude Code plugin's
PreToolUse hook reports findings on
pending commits, pushes, and PR creation. It suggests /review-a11y; the user chooses
whether to invoke the skill. The standalone init --hook pre-commit gate runs the
engine without loading an agent skill.
Do not
- Invent a non-conformity the engine did not find and you cannot cite.
- Assert contrast/focus/zoom from source — they are residual risks until rendered (resolve via
scan).
- Mix pre-existing repo issues into the verdict on the change.
- Translate technical tokens in French prose (
aria-live, never « région live »).
1---2name: review-a11y3description: Review changed frontend code for WCAG 2.2 AA accessibility with findings, fixes, and a verdict.4license: MIT5---67# review-a11y — accessibility review of changed code89The review counterpart of the `ultra11y` skill: where `ultra11y` produces full audits and10compliance deliverables, **this skill reviews exactly the code under change** — a diff, a11branch, a PR — and reports like a code reviewer. Same division of labour: the12deterministic, install-free engine bundled with this skill does the *mechanical* work13(machine-checkable WCAG 2.2 AA non-conformities, each tied to a success criterion, cited14`file:line`), and **the AI agent** *adjudicates the judgment* — refute false positives, decide15alt relevance/link purpose/focus logic statically from the code, and route what needs a rendered16DOM to `scan`. A human is at most optional oversight.1718**Engine resolution:** every command below is `node scripts/ultra11y.mjs …` where19`scripts/ultra11y.mjs` lives **next to this SKILL.md** (the engine ships inside the skill;20no install, no key). Run it with the reviewed project as the working directory. In the21ultra11y repository itself, the repo root's `scripts/ultra11y.mjs` is the same file.22On Claude Code that path is `${CLAUDE_SKILL_DIR}/scripts/ultra11y.mjs`; on Codex and23OpenCode it is the skill directory the plugin installed. If neither resolves — a harness24with no skill system — `npx -y ultra11y <command>` is the same engine. Per-harness deltas:25`references/harnesses.md` in the `ultra11y` skill.2627> **Core rules** (shared with `ultra11y`):28> 1. **Never invent a non-conformity**: report only what the engine found or what you can29> see and cite in the code under review.30> 2. **Review the change, not the world**: findings outside the reviewed scope are noted in31> one line at most ("pre-existing, out of scope"), never mixed into the verdict.32> 3. **Residual is explicit, never silently conforming**: the *rendering* criteria (contrast,33> focus visibility, zoom/reflow, content-on-hover) are named as residual risks for the changed34> components — never asserted from source, resolved in the rendered page (`scan`). The35> *judgment* criteria (alt relevance, link purpose, reading order) the AI agent adjudicates36> from the code itself, recording a reason — no status without a justified verdict.37> 4. **Language**: write the review in the conversation's language, but technical tokens38> stay in English even in French prose — `aria-live` stays `aria-live` (never39> « région live »), same for `tabindex`, `alt`, `role="alert"`, landmark role names.4041## 1. Scope — review what changed4243Pick the FIRST that applies (ask only when genuinely ambiguous):4445```46node scripts/ultra11y.mjs audit <files the user named> --jsx --json # explicit files47node scripts/ultra11y.mjs audit --staged --graph --json # staged snapshot (pre-commit review)48node scripts/ultra11y.mjs audit --changed --graph --json # working diff49node scripts/ultra11y.mjs audit --since origin/main --graph --json # the whole branch (PR review) — use the repo's default branch50```5152- Always pass `--graph` for JSX/TSX (real AST + cross-file rules: icon-only component used53 without a name, anchor target defined in another file…); it also suppresses a class of54 single-file false positives.55- Pass `--lang` matching the conversation so the engine's message/remediation strings are56 in the review's language (unset, the CLI auto-detects — a fallback, not a substitute).57- Whole-repo review is an **audit**, not a review — offer the `ultra11y` skill instead.58- Test/story markup (`*.test.*`, `*.stories.*`, `__tests__/`) is excluded by default; name59 such a file explicitly to include it, or pass `--no-default-excludes`.6061## 2. Judge — the engine's findings are candidates, not verdicts6263For each finding (`--json` gives `criteria`, `severity`, `file:line`, `message`,64`remediation`):6566- **Confirm the occurrence**: open the cited line; the offending element must really be67 there in the change under review.68- **`preliminary: true` findings** (`.vue`/`.svelte`/`.astro` source, library components):69 the template parser cannot see slots, conditionals or provide/inject wiring. Confirm70 against the rendered DOM if captures exist (`.ultra11y/captures`, from the `ultra11y`71 skill's `render --setup` harvester), otherwise report them as **provisional** with that72 caveat — or refute them with evidence from the code.73- **Library-rendered JSX (DSFR, MUI…)**: a clean source review can still ship broken HTML74 (false negatives) — say so explicitly when the change touches such components, and75 recommend a rendered-DOM check (`scan`, or the `ultra11y` capture pipeline).76- **The judgment criteria are the AI agent's to adjudicate**: alt text relevance, link purpose77 in context, visible label vs accessible name, keyboard/focus logic of the changed component78 (read the full component source, not just the flagged line), recording a reason for each call.79- Refuting a finding requires **cited evidence** (the code that disproves it); silently80 dropping findings is forbidden.8182## 3. Report — a review, not a compliance document8384Render in the conversation's language (Core rule 4), grouped by severity:8586```87## Accessibility review — <scope, e.g. branch feat/x vs main, 12 files>8889### 🔴 Blocking90- `src/components/Form.tsx:42` — **3.3.2 Labels or Instructions** — input has no91 label and placeholder is its only hint → associate a real <label for>, keep the92 placeholder as an example only.9394### 🟠 Major / 🟡 Minor95- …same shape…9697### Residual risks (rendering-dependent — resolve in the rendered page via scan)98- contrast of the new button variants (1.4.3), focus visibility on the custom menu (2.4.7)99100### Verdict1011 blocking, 2 major to fix before merge; 2 provisional SFC findings to confirm on the102rendered DOM; residual risks listed above.103```104105- Keep the engine's `remediation` text as the base of each fix — it is already localized106 and criterion-grounded.107- Pre-existing issues spotted in passing go in ONE final line, out of the verdict.108109**Running as a subagent.** When the user explicitly requests this review in a subagent,110return the complete report above to the caller. A hook or another skill may suggest111this review; wait for the user's explicit invocation before loading it.112113## 4. After the review (opt-in)114115- **Safe mechanical fixes**: `fix --staged --write --safe` (auto-applies the safe codemods116 to staged files and re-stages them) or `node scripts/ultra11y.mjs fix <files> --write`;117 dry-run without `--write`.118- **Formal deliverables** (dated report, auditor conformance blocks, PRD backlog, GitHub119 issues, RGAA/country-standard vocabulary): that is the `ultra11y` skill — same engine,120 same findings (`report`, `prd`, `check`/`verify` gates).121- **Mechanical gate**: the Claude Code plugin's `PreToolUse` hook reports findings on122 pending commits, pushes, and PR creation. It suggests `/review-a11y`; the user chooses123 whether to invoke the skill. The standalone `init --hook` pre-commit gate runs the124 engine without loading an agent skill.125126## Do not127128- Invent a non-conformity the engine did not find and you cannot cite.129- Assert contrast/focus/zoom from source — they are residual risks until rendered (resolve via `scan`).130- Mix pre-existing repo issues into the verdict on the change.131- Translate technical tokens in French prose (`aria-live`, never « région live »).