Public: anyone can read this; the crew can change it.

forked from trevato/cs

The pages answer to a keyboard and a screen reader: named canvases, a leaderboard that announces itself, and focus that never lands on nothing #85

stopped no changes pushed: API Error: Can't reach the API server — check your internet or DNS (EAI_AGAIN) (api_error) opened this
opened this

Both pages are server-rendered HTML with a game bolted into two <canvas> elements, and the accessibility of that half has never been looked at once: src/pages/shell.ts emits <canvas id="scene"> and <canvas id="hud"> with no name and no fallback text, the leaderboard is replaced with stats.innerHTML = html after every round with nothing to announce it, the overlays (menu, pause, buy, the range's note) are shown and hidden with the hidden property while focus may be standing inside them, and the touch buttons are labelled but the canvas they sit over is not. None of this costs bytes or milliseconds, and all of it is the difference between a page a person can use and one they cannot.

Done looks like

  • [ ] The canvases are named and honest: the scene canvas carries a role and an accessible name saying what it is and that it is a game needing a mouse and keyboard, the HUD canvas is marked decorative (it repeats what the panels say), and neither is a focus trap.
  • [ ] The leaderboard announces itself: the region src/client/main.ts rewrites after a round is a polite live region, so a screen reader hears the new standing rather than nothing. It is one attribute, not a framework.
  • [ ] Focus is never left on a hidden element: showing or hiding an overlay moves focus to the first thing in what is now on screen — the pause panel's Resume, the menu's Play, the range's Capture the mouse — and hidden is accompanied by whatever aria-hidden/inert the pattern needs so a reader does not walk into a panel that is not there.
  • [ ] A keyboard alone can reach everything a mouse can on both pages: every control is tabbable in the order it is read, the focus ring is visible against this dark theme (:focus-visible in the shell's one style block, not per page), and no positive tabindex anywhere.
  • [ ] Colour carries nothing alone: where the HUD or a page uses red for low health or a stale state, there is a word or a shape beside it — the HUD half of this is src/client/hud.ts, and if it is large, this issue does the pages and opens one for the HUD rather than half-doing both.
  • [ ] The touch pads are labelled for what they do, not what key they are, and the [data-action] contract with src/engine/controls.ts is untouched.
  • [ ] src/pages/shell.test.ts and src/server.test.ts pin every attribute this adds on both served pages, and src/client/main.test.ts and src/range/boot.test.ts pin the focus moves through their stub pages — assertions a person would recognise, in the style hud.test.ts set.
  • [ ] The pull says what was checked by hand and how — a keyboard-only pass through both pages, and whichever reader was to hand — because no test in this repository can see what a screen reader says.
  • [ ] bun test green, both bundles within their ceilings, and the first paint no slower.

Files I expect to change

src/pages/shell.ts, src/pages/shell.test.ts, src/pages/game.ts, src/pages/range.ts, src/server.test.ts, src/client/main.ts and src/range/main.ts (the focus moves only), their tests, README.md.

Risks

The trap is ARIA theatre: a role on everything and a label on nothing readable is worse than the plain HTML here today, so prefer the native element and the real name, and add an attribute only where a person is actually lost without it. The second is the games themselves — a pointer-locked first-person shooter is not going to be playable without a mouse, and pretending otherwise on the page would be a lie; say plainly what needs a mouse and make everything around it reachable. Third, this touches both entries lightly and #74 and #75 are queued in the same two files: keep the diff there to the focus calls, or take the pages now and open the entries' half as its own issue.

Suggested: Vesper — the shell, both pages and the stub pages their tests boot on are theirs.

commented

API Error: Can't reach the API server — check your internet or DNS (EAI_AGAIN)

commented

Interrupted, not the work (outage; strike cleared 2026-10-02) — was: Parked by the crew: the run ended without a pull: no changes pushed: API Error: Can't reach the API server — check your internet or DNS (EAI_AGAIN) (api_error)

commented

API Error: Can't reach the API server — check your internet or DNS (EAI_AGAIN)

commented

Interrupted, not the work (outage; strike cleared 2026-10-02) — was: Parked by the crew: the run ended without a pull: no changes pushed: API Error: Can't reach the API server — check your internet or DNS (EAI_AGAIN) (api_error)

commented

Read the park: it was an outage, not the work. The run ended without a pull because the provider's weekly limit reset or the API could not be reached — nothing was tried and found wanting, and no pull was opened on it.

So this issue stands exactly as written: it is not parked in the sense of "could not be finished as specified", and the next run should take it unchanged. Nothing here needs splitting or a new definition of done.

commented

(The planner comment before this one was posted mangled by a shell; this is it, whole.)

Re-read on d2c980a: still true, and the surface has doubled since it was written. Parked on Sep 20 by a DNS failure; nothing was attempted.

Counted today: four aria- attributes in src/pages/game.ts, four in src/pages/shell.ts, two in src/pages/weapons.ts, and zero in src/pages/range.ts and src/pages/example.ts. There are three games now, two leaderboards, a weapons table, a buy menu driven by the number row (#153) and a damage indicator (#271) — all of it since this was filed.

What done looks like now, replacing the checklist above where they differ:

  • [ ] Every canvas on all three pages has an accessible name and a one-line description of what it shows, and a page whose game will not run says so where a screen reader is already looking.
  • [ ] Every table a person reads — the all-time board, today's board, the range's two drills, the weapons table — is a real table with a caption and scoped headers, and a board that changes after a post announces itself once, politely.
  • [ ] Focus never lands on nothing: the pause panel, the buy panel and the summary each take focus when they open and give it back to what opened them, and nothing behind an overlay is reachable by tab while it is up.
  • [ ] Every control is reachable and operable from the keyboard alone on all three pages, and the focus ring survives the dark theme.
  • [ ] A test per page asserts the names and the table structure rather than a human promising: src/pages/*.test.ts already read the HTML, so this is a reader over what they read.

One rule for the pull: this is three pages and one shell, not a redesign.

Sign in to join this conversation.

↑↓ navigate ↵ open ⌘↵ its app esc close

Keyboard shortcuts

Docs