Files
atomic-design-poc/docs/backlog/WP-17-app-a11y.md
Edwin van den Houdt e82309786d style: format frontend, docs and skills with prettier; add .prettierignore
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>
2026-07-03 13:39:31 +02:00

3.6 KiB
Raw Blame History

WP-17 — App-level a11y: route focus/scroll, template lint, WCAG checklist + MDX

Status: todo Phase: 4 — a11y

Why

Three app-level gaps close the WCAG story:

  • No route-change focus/scroll managementapp.config.ts has only provideRouter(routes, withViewTransitions()); after navigation, focus stays wherever it was and scroll position is unmanaged. (The wizard manages focus within steps; the skip link is the only cross-page mechanism.)
  • No template a11y linting@angular-eslint is entirely absent.
  • User decision: a manual WCAG checklist documents what automation can't test.

Read first

  • src/app/app.config.ts
  • eslint.config.mjs, package.json
  • src/app/shared/layout/shell/shell.component.ts (#main target), page-shell/page-shell.component.ts (candidate focus target)

Decisions (pre-made, don't relitigate)

  • Scroll: withInMemoryScrolling({ scrollPositionRestoration: 'enabled' }).
  • Focus: on NavigationEnd, move focus to the new page's h1 (or #main fallback, tabindex="-1"), deferred via afterNextRender so it coexists with view transitions; skip the initial load. Implement as a small root service/effect wired in app.config.ts — not per page.
  • Lint: angular-eslint at the version matching Angular 22 (~v21+ — check npm view angular-eslint peerDependencies), with processInlineTemplates (this repo has no .html templates). Rules: alt-text, label-has-associated-control, interactive-supports-focus, click-events-have-key-events, valid-aria, elements-content. Verify the processor fires by planting a known-bad pattern before trusting green.
  • The checklist is a living doc with per-page status columns — not prose.

Files

  • src/app/app.config.ts + a new small shared/ focus-on-navigation service
  • eslint.config.mjs, package.json (angular-eslint)
  • Any template the new lint rules flag
  • New docs/wcag-checklist.md
  • New src/docs/a11y.mdx — title Foundations/Accessibility

Steps

  1. Router scroll + focus service; manual keyboard smoke (tab through, navigate, confirm focus lands on the new page's heading).
  2. Add angular-eslint; enable the template rules on inline templates; plant a bad pattern, see it fail, remove it; fix real hits.
  3. docs/wcag-checklist.md: manual checks per page (dashboard, wizards, brief, login) — keyboard walk & focus order, no traps, 200% zoom/reflow, NVDA or VoiceOver pass, visible focus, error announcement; status columns (page × check).
  4. src/docs/a11y.mdx: the layered approach — axe gate (WP-01) + template lint + play tests (WP-16) + manual checklist; the alert-role rationale; where the a11y.disable skip register lives and its rules.

Acceptance criteria

  • Navigating between routes moves focus to the new page's heading; scroll resets; view transitions still play.
  • Template a11y rules active and proven to fire; lint green.
  • Checklist checked in with an initial pass filled in for the dashboard at minimum.
  • a11y.mdx renders; links to checklist and WP-01 skip rules.

Verification

GREEN + npm run test-storybook:ci. Manual: keyboard-only walk of login → dashboard → wizard → brief.

Out of scope

Full manual audit of every page (the checklist enables it; filling it beyond the dashboard is ongoing work, not this WP).

Risks

  • angular-eslint's inline-template processor silently not running → the planted-violation step is mandatory.
  • Focus-move vs withViewTransitions timing → afterNextRender + test in a real browser (npm start), not just unit-level.