642c5b7322
Add the a11y foundation and bring login, lobby, and the in-game shell to WCAG 2.2 AA: - Primitives: .sr-only + .skip-link (base.css), trapFocus (modal focus trap + restore) and restoreFocus (menu focus restore) actions, the --color-focus visible ring. - In-game shell: skip link + focusable main landmark; WAI-ARIA sidebar tabs (roving tabindex, arrow/Home/End, tabpanel wiring); menu Escape + focus restore (account / view / turn-navigator / map-toggles / bottom-tabs); mail compose as a role=dialog modal with a focus trap. - login / lobby / lobby-create: skip link + main landmark, field labels, role=alert / role=status live regions. - Map canvas: aria-label naming it a visual overview, with its data reachable by keyboard via the sidebar inspector and tables (accessible alternative; in-canvas keyboard nav deferred). Gates (chromium-desktop): tests/e2e/a11y-axe.spec.ts scans every top-level view for WCAG 2.2 AA violations (zero); a11y-keyboard.spec.ts covers the skip link, menu Escape+restore, and tab roving. Adds @axe-core/playwright. Docs: ui/docs/a11y.md (+ index). Marks F1 and F2 done in ui/PLAN-finalize.md. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
3.9 KiB
3.9 KiB
Accessibility
The client targets WCAG 2.2 AA. This doc records how accessibility is built in, what is verified automatically, and the deliberate boundaries.
Tooling & gates
- axe-core via Playwright (
@axe-core/playwright):tests/e2e/a11y-axe.spec.tsscans every top-level view — login, lobby, lobby/create, and the in-game map / report / mail / battle / science-designer / table — with thewcag2a wcag2aa wcag21a wcag21aa wcag22aarule tags and asserts zero violations. It runs once, on thechromium-desktopproject (axe's contrast and computed-role checks need one real engine; the webkit/mobile projects add cost without new signal). - Keyboard navigation (Playwright):
tests/e2e/a11y-keyboard.spec.tscovers the skip link, Escape-closes-and-restores-focus on menus, and the sidebar tab arrow-key pattern. - Svelte compiler a11y lint is on (no suppressions);
pnpm checkfails on a11y warnings, so basic role/label/structure issues are caught at build time.
Shared primitives
.sr-only(inbase.css) — visually hidden, available to assistive tech..skip-link— visible only on focus; each layout renders one as its first focusable element, pointing at atabindex="-1"main region.trapFocus— Svelte action for modal dialogs: moves focus in (to[data-autofocus]), cycles Tab/Shift+Tab within, restores focus on close.restoreFocus— Svelte action for non-modal popovers/menus: returns focus to the trigger when the surface closes, without trapping.--color-focustoken drives a single visible focus ring app-wide (:where(*):focus-visibleinbase.css).
Coverage by area
- Landmarks. The in-game shell is
header(banner) +main+aside(complementary); login/lobby/create each wrap content in amain#main-content. Every layout has a skip link to its main. - Forms (login, lobby, create). Every control has an associated
label; submit/validation errors use
role="alert", async status usesrole="status", so they are announced. - Sidebar tabs follow the WAI-ARIA tabs pattern:
role="tablist"/tab/tabpanel, rovingtabindex, arrow + Home/End keys with automatic activation,aria-controls/aria-labelledbylinking tabs to the panel. - Menus & popovers (account, view, turn navigator, map toggles,
mobile "more"):
aria-haspopup/aria-expandedtriggers, Escape and outside-click dismissal, andrestoreFocusso focus is never dropped to<body>. - Modal dialog (mail compose):
role="dialog"+aria-modal+aria-labelledby,trapFocus, Escape to close.
The map canvas
The PixiJS/WebGL map is a visual surface that cannot be made keyboard- or
screen-reader-navigable without a bespoke effort. It is therefore handled
as an accessible alternative: the <canvas> carries an aria-label
naming it and pointing to where the same information lives, and every
datum it shows — planets, ship groups, routes — is reachable by keyboard
through the sidebar inspector and the tables view. In-canvas keyboard
navigation (cursoring between planets) is intentionally out of scope; it
is noted as a future enhancement in ROADMAP.md.
Boundaries / future work
- The deep AA pass focuses on login, lobby, and the in-game shell (acceptance surfaces). Secondary views pass the axe structural scan; exhaustive keyboard/SR polish of every view interaction is incremental.
- The battle scene (
battle-player/battle-scene.svelte) is a data-viz surface (seedesign-system.md); its content is summarised by the adjacent text protocol log. - In-canvas keyboard navigation for the map (deferred, above).