Skip to content

fix(game): rematch focus, named position bar, still first load - #308

Merged
arrzdev merged 5 commits into
mainfrom
fix/a11y-rematch-eval-cls
Nov 9, 2026
Merged

arrzdev merged 5 commits into
mainfrom
fix/a11y-rematch-eval-cls

Conversation

@arrzdev

@arrzdev arrzdev commented Nov 9, 2026 •

Copy link
Copy Markdown
Owner

What and why

Three small accessibility and stability fixes on the offline game, found in an audit of keyboard, screen reader and desktop paths. Nothing looks different.

  • Rematch keeps focus on the board. Pressing Rematch (from the Game over sheet, or after closing it with Escape) used to drop focus to the top of the page, and the board read out the last game's cell. The board now takes focus at its centre once the new game is playable, and its cell announcement stays quiet until a key moves the cursor.
  • The position bar is named by what it says. Both the phone bar and the column beside the board were named just "Level". They now read "Position: even" or "Position: Black ahead by 1.2", from one rounding rule shared with the number drawn on the bar. The "Level" tooltip is gone, and the old eval_level and eval_advantage strings are replaced in all 13 locales.
  • No layout shift on a first load of /offline on desktop. The unpainted canvas is a 300x150 box that jumped to the board when the script ran (0.117 at 1280x800 in Chromium). It is now out of flow until its first draw.

The home preload budget goes up by 293 bytes for the board's new keyboard reset and its separate live region.

How to verify

  • pnpm exec playwright test e2e/rematch-focus.spec.ts e2e/position-bar.spec.ts e2e/offline-load-shift.spec.ts e2e/phone-play-a11y.spec.ts e2e/offline-first-paint.spec.ts
  • pnpm --filter @repo/game exec vitest run src/utils/evaluation.test.ts
  • Chromium passes locally (a wider set of 20 related specs too). WebKit runs through the E2E WebKit workflow.

UI quality gate

  • No layout shift: click, open, hover, focus, loading → loaded, font swap (new cold-load spec, 0 above 0.01)
  • Space reserved for text that appears later (status, error, feedback) (no new text; the names are aria only)
  • No hard-clipped text: ellipsis or wrap, never cut mid-character (no visible text changed)
  • No text hidden by its container, the viewport, or a sticky bar (no visible text changed)
  • Modals and dialogs fit without scrolling at 375x667 (not touched)
  • Focus ring fully visible on every control (the board's ring is unchanged; focus now lands on it after a rematch)
  • Touch targets ≥ 44x44 on phone (not touched)
  • Empty, loading and error states exist and look designed (not touched)
  • Every network action shows a pending state: spinner, busy, disabled, one submit (no network action added)
  • Button labels stay on one line at 320 in the longest locale (no button label changed)
  • Checked at 375x667, 402x710 and desktop, Chromium + WebKit (375x667 and 1280x800 on Chromium; WebKit pending the workflow run)
  • New UI spec uses the shift and clipped-text helpers (the cold-load spec measures layout-shift entries directly, since the helpers measure boxes after an action, not a page load)

- "Position: even" or "Position: Black ahead by 1.2", built from the score
  in the bar components, one rounding for the name and the drawn figure
- drop the Level tooltip and the unused eval_level and eval_advantage keys
  in all 13 locales
- unit test for the formatter, e2e for the phone bar and the desktop column
- the board resets its keyboard cursor to the centre on a rematch, and the
  live region stays empty until a key moves the cursor, so the last game
  cell is not read out
- the offline page focuses the board once the new game is playable, and
  the Game over sheet hands focus to it instead of the removed button
- raise the home preload budget by the 171 bytes this adds
- the canvas is out of flow until its first draw, so the 300x150 default
  box no longer jumps to the board when the script runs
- e2e: no layout shift above 0.01 on a cold load at 1280x800

@arrzdev arrzdev left a comment

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Approve. The bar figure and its name come from one tested rounding rule. Rematch focus is covered on both the sheet path and the Escape path, and the cold-load shift spec measures layout-shift entries directly. Green on top of main (the #307 axe checks included) and in the WebKit run.

@arrzdev
arrzdev merged commit 0e8e3ce into main Nov 9, 2026
5 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant