.agents/skills/ibm-a11y-pr-remediation/SKILL.md
Scope is IBM Equal Access Level 1 only. Default mode is fix, not report-only. Scan every frontend surface touched by the PR/branch and remediate all in-scope issues until both engines are green (or only documented baselines remain).
This skill is a PR-scoped orchestrator. It does not duplicate detailed engine/pattern guidance — read the linked skills for that:
src/frontend/** changes.page.runA11yScan and/or scripts/a11y/a11y_scan.py — see ibm-a11y-testing-guide / ibm-a11y-route-scan).ibm-a11y-level1-audit).Do not invent new tag names, silently disable scans, or expand into IBM Level 2/3 unless asked.
IBM L1 PR A11y:
- [ ] 1. Collect changed frontend files
- [ ] 2. Map files → surfaces / routes / states
- [ ] 3. Scan (IBM + axe)
- [ ] 4. Fix all in-scope violations
- [ ] 5. Add/update tests if needed
- [ ] 6. Re-scan assert-green
- [ ] 7. Report back
Prefer the PR merge base when a PR exists; otherwise the branch merge base vs main/master.
# PR number known
gh pr diff <n> --name-only | grep -E '^src/frontend/' || true
# Current branch vs upstream default
BASE=$(git merge-base HEAD origin/main 2>/dev/null || git merge-base HEAD origin/master)
git diff --name-only "$BASE"...HEAD -- 'src/frontend/**'
# Include uncommitted work when the user is mid-change
git diff --name-only HEAD -- 'src/frontend/**'
git diff --name-only --cached -- 'src/frontend/**'
git ls-files --others --exclude-standard 'src/frontend/**'
Include:
src/frontend/src/**/*.{tsx,ts,jsx,js,css} (UI)src/frontend/tests/a11y/** (existing coverage)Skip pure non-UI churn unless it affects a11y (e.g. test helpers that change focus / ARIA). If no frontend files changed, say so and stop.
For each changed file, identify:
| Change type | Scan target |
|---|---|
| Page / route | That route + meaningful states (empty/populated/modal/mobile) |
Shared component (TableComponent, dialogs, menus) | Every consumer page that uses it — not only the file you touched |
| Primitive | Jest axe on the primitive + any Playwright surface that embeds it |
| Spec / baseline only | Re-run that spec; no product fix unless it fails |
a11y_routes.json | Update static coverage; run static or route scan |
List interactive controls and states (default, empty, populated, open modal / menu, selected row, error, mobile). Prefer existing specs under src/frontend/tests/a11y/.
Automated a11y is not one tool — see ibm-a11y-testing-guide for the full engine comparison and gotchas. Summary:
axe() (@/utils/a11y-test), jsdom-only.page.runA11yScan(label) for stateful surfaces (modals/menus/selected/editing); scripts/a11y/a11y_scan.py for default-loaded page only.cd src/frontend
RUN_A11Y=true RUN_A11Y_ASSERT=true npx playwright test tests/a11y/<feature>.a11y.spec.ts --project=chromium --workers=5
# Python scanner playwright deps are NOT in default uv sync.
# One-time: uv run --with playwright playwright install chromium
uv run --with playwright python scripts/a11y/a11y_scan.py \
--url http://localhost:3000 \
--routes /settings/<route> \
--out /tmp/a11y.json --markdown /tmp/a11y.md --timeout-ms 45000
RUN_A11Y=true runs the scan; RUN_A11Y_ASSERT=true fails on new violations. After changing a shared component, re-scan every page that uses it.
For component-only changes:
cd src/frontend
npx jest path/to/<name>.a11y.test.tsx --runInBand
Do not invent findings — prefer scanner output plus manual Level 1 spot checks scanners miss (keyboard trap both ways, focus restore, 320px reflow, color-not-only).
Default: fix. Only list proposed fixes without editing if the user asked report only — then hand off to ibm-a11y-level1-audit for the formal report format.
Rules:
ibm-a11y-testing-guide (AG Grid, Radix asChild, focus restore, icon-only aria-label).aria-labels through i18n (t(...), all locale files) per frontend-i18n.src/frontend/tests/a11y/baselines/ only for documented framework debt.ibm-a11y-level1-audit/references/ibm-level1-criteria.md) unless the user expands scope.| Surface | Spec |
|---|---|
| Static routes | static-routes.a11y.spec.ts (+ scripts/a11y/a11y_routes.json) |
| Auth | auth-pages.a11y.spec.ts |
| Core pages | core-pages.a11y.spec.ts |
| Data-rich | files.a11y.spec.ts, api-keys.a11y.spec.ts, global-variables.a11y.spec.ts |
| Other data-rich | data-rich-routes.a11y.spec.ts |
If you fixed a state with no scan, add one (and keyboard tests for custom keyboard behavior) following files.a11y.spec.ts / api-keys.a11y.spec.ts patterns.
Tag every Playwright a11y test @release plus a domain tag (@workspace / @api / @database / @components / @starter-projects). Import test/expect from ../fixtures.
Re-run the same scans with RUN_A11Y_ASSERT=true. Each finding → fixed | baselined | open.
When done, state: