One-time prettier --write so the new format:check CI gate starts green. .prettierignore excludes generated (api-client.ts, documentation.json), vendored (public/cibg-huisstijl), and backend (dotnet format owns it). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
3.6 KiB
3.6 KiB
WP-01 — Axe-on-every-story CI gate
Status: done (97f7de4)
Phase: 0 — enforcement & gates
Why
The Storybook a11y addon (@storybook/addon-a11y, configured in .storybook/preview.ts
for wcag2a, wcag2aa, wcag21a, wcag21aa) only surfaces violations interactively.
Nothing gates CI. This WP turns "a panel you can look at" into "a check that fails the
build", so every story added or changed by later WPs is automatically covered.
Read first
.storybook/main.ts,.storybook/preview.ts(existing a11y config — reuse its tags).github/workflows/ci.yml(job layout to extend)package.json(scripts; note.npmrchaslegacy-peer-deps=true)
Decisions (pre-made, don't relitigate)
- Mechanism:
@storybook/test-runner+axe-playwrightagainst the static build. The repo uses@storybook/angularon the webpack builder, which rules out@storybook/addon-vitest(Vite-only). Fallback if test-runner doesn't support Storybook 10 yet: a hand-rolled Playwright +axe-coreloop overstorybook-static(same gate semantics — document the swap in this file if taken). - Escape hatch:
parameters: { a11y: { disable: true } }per story, allowed only with an inline justification comment and a cross-reference to the WP that will fix it (WP-16/WP-17).
Files
package.json(devDependencies + scripts).storybook/test-runner.ts(new).github/workflows/ci.yml(new job)- Possibly a handful of story/component files for triaged violations
Steps
- Check version fit first:
npm view @storybook/test-runner peerDependenciesand pick the release matching the installed Storybook 10 (legacy-peer-depsabsorbs minor lag). npm i -D @storybook/test-runner axe-playwright http-server wait-on concurrently(+npx playwright install chromiumlocally)..storybook/test-runner.ts:preVisit:injectAxe(page).postVisit:getStoryContext(page, context); skip whenparameters.a11y?.disable === true; elsecheckA11y(page, '#storybook-root', …), honoringparameters.a11y.config/options.runOnlyfrompreview.ts(don't duplicate the tag list — read it from the story context).
- Scripts:
"test-storybook": "test-storybook""test-storybook:ci": "concurrently -k -s first -n sb,axe \"http-server storybook-static -p 6006 --silent\" \"wait-on tcp:127.0.0.1:6006 && test-storybook --url http://127.0.0.1:6006\""
- CI job
storybook-a11yinci.yml: checkout → setup-node (same version as thefrontendjob) + npm cache →npm ci→npx playwright install --with-deps chromium→npm run build-storybook→npm run test-storybook:ci. - Run locally against a fresh
build-storybook; triage violations: fix trivial ones (labels, roles, contrast via--rhc-*tokens); anything structural gets the escape hatch + comment + WP cross-ref. - Update
docs/backlog/README.md: GREEN now includesnpm run test-storybook:ci.
Acceptance criteria
npm run test-storybook:cipasses locally against a freshstorybook-static/.- CI has a
storybook-a11yjob; an axe violation makes it fail (verified once with a deliberately broken story, then reverted). - Every
a11y.disableskip carries a justification comment + WP cross-ref. - Backlog README GREEN updated.
Verification
GREEN + npm run test-storybook:ci.
Out of scope
Fixing structural a11y issues in components (WP-16/WP-17); Playwright e2e page sweeps (explicitly not chosen).
Risks
- test-runner ↔ Storybook 10 peer mismatch → use the documented fallback.
- Unknown violation count on first run → the escape hatch keeps the WP bounded.