# 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.ts`](../frontend/tests/e2e/a11y-axe.spec.ts) scans every top-level view — login, lobby, lobby/create, and the in-game map / report / mail / battle / science-designer / table — with the `wcag2a wcag2aa wcag21a wcag21aa wcag22aa` rule tags and asserts zero violations. It runs once, on the `chromium-desktop` project (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.ts`](../frontend/tests/e2e/a11y-keyboard.spec.ts) covers 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 check` fails on a11y warnings, so basic role/label/structure issues are caught at build time. ## Shared primitives - `.sr-only` (in [`base.css`](../frontend/src/lib/theme/base.css)) — visually hidden, available to assistive tech. - `.skip-link` — visible only on focus; each layout renders one as its first focusable element, pointing at a `tabindex="-1"` main region. - [`trapFocus`](../frontend/src/lib/a11y/focus-trap.ts) — Svelte action for modal dialogs: moves focus in (to `[data-autofocus]`), cycles Tab/Shift+Tab within, restores focus on close. - [`restoreFocus`](../frontend/src/lib/a11y/restore-focus.ts) — Svelte action for non-modal popovers/menus: returns focus to the trigger when the surface closes, without trapping. - `--color-focus` token drives a single visible focus ring app-wide (`:where(*):focus-visible` in `base.css`). ## Coverage by area - **Landmarks.** The in-game shell is `header` (banner) + `main` + `aside` (complementary); login/lobby/create each wrap content in a `main#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 uses `role="status"`, so they are announced. - **Sidebar tabs** follow the WAI-ARIA tabs pattern: `role="tablist"` / `tab` / `tabpanel`, roving `tabindex`, arrow + Home/End keys with automatic activation, `aria-controls` / `aria-labelledby` linking tabs to the panel. - **Menus & popovers** (account, view, turn navigator, map toggles, mobile "more"): `aria-haspopup` / `aria-expanded` triggers, Escape and outside-click dismissal, and `restoreFocus` so focus is never dropped to `
`. - **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 `