The account menu's identity block and theme strip are valid menu children, and its name is readable (#745) #747

Merged
claude-bot merged 1 commit from fix/account-menu-aria into main 2026-09-22 05:40:06 +00:00
Collaborator

Fixes #745, found by the journeys harness's axe gate on its first day (#726).

  • The identity block leaves the role="menu" region. Menu now renders its head slot as a sibling before the element carrying role="menu" (a new inner .menu__panel), and AccountMenu passes the disc, name and pronouns as head. A menu may own only menu items, groups and separators.
  • The theme strip is a role="group" of three menuitemradio buttons (aria-checked), drawn with the segmented strip's own classes so nothing changes on screen. Left/Right/Home/End move within the strip; Up/Down still belong to the menu, which treats the strip as one stop (generic in Menu, keyed on a nested role="group", so any future consumer gets it).
  • The panel's ink. .menu sets color: var(--ink): it sits inside .appbar, which sets color: var(--chrome-ink) for the dark chrome, and the name and pronouns inherited it at 1.14:1 on white.
  • src/shell/AppFrame.jsx: the two literal NUL bytes in the search cache key are | now, so git and grep stop treating the file as binary.

One deliberate visual delta: the identity block's vertical padding moves from --sp-4 to the shared .menu__head's --sp-3, since it now sits in that box rather than carrying its own.

Tests: 1,158 green (new: the block outside the menu role; the group and its radios; Right moves focus without picking; Down leaves the strip for Sign out). Lint clean. The harness's a11y-baseline.js (#746) drops its two BUG entries once both are on main.

🤖 Generated with Claude Code

Fixes #745, found by the journeys harness's axe gate on its first day (#726). - **The identity block leaves the `role="menu"` region.** `Menu` now renders its `head` slot as a sibling before the element carrying `role="menu"` (a new inner `.menu__panel`), and `AccountMenu` passes the disc, name and pronouns as `head`. A `menu` may own only menu items, groups and separators. - **The theme strip is a `role="group"` of three `menuitemradio` buttons** (`aria-checked`), drawn with the segmented strip's own classes so nothing changes on screen. Left/Right/Home/End move within the strip; Up/Down still belong to the menu, which treats the strip as one stop (generic in `Menu`, keyed on a nested `role="group"`, so any future consumer gets it). - **The panel's ink.** `.menu` sets `color: var(--ink)`: it sits inside `.appbar`, which sets `color: var(--chrome-ink)` for the dark chrome, and the name and pronouns inherited it at 1.14:1 on white. - `src/shell/AppFrame.jsx`: the two literal NUL bytes in the search cache key are `|` now, so git and grep stop treating the file as binary. One deliberate visual delta: the identity block's vertical padding moves from `--sp-4` to the shared `.menu__head`'s `--sp-3`, since it now sits in that box rather than carrying its own. Tests: 1,158 green (new: the block outside the menu role; the group and its radios; Right moves focus without picking; Down leaves the strip for Sign out). Lint clean. The harness's `a11y-baseline.js` (#746) drops its two `BUG` entries once both are on main. 🤖 Generated with [Claude Code](https://claude.com/claude-code)
fix(frontend): the account menu's identity block and theme strip are valid menu children (#745)
All checks were successful
CI / Backend lint (ruff) (pull_request) Successful in 1m12s
CI / Feedback site tests (pull_request) Successful in 1m26s
CI / Bot/backend version sync (pull_request) Successful in 22s
CI / Summarisation accuracy eval harness (stub provider) (pull_request) Successful in 1m45s
CI / Bot tests and audit (pull_request) Successful in 2m54s
CI / Frontend tests, audit, and build (pull_request) Successful in 3m4s
CI / Docker image build (pull_request) Successful in 4m25s
CI / Backend migration, tests, and audit (pull_request) Successful in 14m52s
CI / Synthetic session harness (no GPU, no LLM) (pull_request) Successful in 13m44s
ab1e715ee1
Menu's `head` slot now renders outside the role="menu" element instead of
inside it, so a menu's owned elements are only group/menuitem* as ARIA
requires. AccountMenu passes its identity block (disc, name, pronouns) as
`head` instead of as the first child, unchanged on screen — Menu.css moves
the panel's box (border, shadow, the flex column) to the outer wrapper and
gives the role="menu" element its own `.menu__panel` layer.

The theme strip is now a `role="group"` of `menuitemradio`s drawn with the
segmented strip's own classes, not a `Segmented` `radiogroup`: Left/Right/
Home/End move the roving focus inside it and stop there; Up/Down bubble to
Menu's own handler, which now collapses a `menuitemradio` group into one
stop so arrowing past the strip is a single press, landing on the group's
checked option when arriving from outside it.

`.menu` gets `color: var(--ink)` — the panel sits inside `.appbar`, which
sets `color: var(--chrome-ink)` for the dark chrome, and the name and
pronouns had been inheriting that at 1.14:1 on white.

Also replaces the two literal NUL bytes in AppFrame's search cache key with
`|`, so git and grep stop treating the file as binary (unrelated to the
menu fix, bundled per the same issue).

e2e/a11y-baseline.js does not exist yet on this branch (arrives with #746),
so its two entries are not touched here.

Verified in Docker (node:24-alpine): npm ci, eslint clean, and the full
vitest suite (104 files, 1158 tests) green.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Sign in to join this conversation.
No description provided.