# 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 management** — `app.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.