SIGN IN SIGN UP

Make the client usable with a keyboard and a screen reader

A 13-area semantic audit found 178 gaps (69 high), and ~10 shared primitives
drove most of them, so the work is concentrated there rather than spread across
call sites: clickable rows, the menu system, tabs, call controls, the media
editor and the stories viewer. Native controls do the activating wherever one
fits; component-specific key handling is reserved for composite navigation.
`attachClickEvent` already wires Enter/Space, but only for `role="button"` —
which is why most clickable-div fixes are just a role and a tabindex.

Keys follow the role rather than whatever helper was nearest. A button answers
to Enter and Space; a link answers to Enter alone, because on a link Space
belongs to the scroll. `<archive-dialog>` is a link, so paging down the chat
list no longer throws you into the archive from one row in the middle, and
`isKeyboardControl` now knows `[role="link"]` as well as `a[href]` so those keys
stop being redirected into the composer.

Focus scopes use the element's owner document, nest, restore through closing
parents, and survive the client being adopted into another window. Hidden,
inert and disabled controls leave the focus order. A scroll earns a tab stop
only when its content cannot be reached otherwise — a chat history or an
article, not a panel of rows that Tab already walks — and it is named when it
gets one.

Increase Contrast is a new opt-in setting, off by default, so ordinary themes
keep their palette.

Semantics being right says nothing about whether a person pressing Tab can SEE
where they are, and reading computed styles cannot answer that: a ring can live
on a pseudo-element or a sibling, and an outline on an `opacity: 0` overlay is
painted and invisible. So `pnpm test:focus` photographs each control holding
focus and again after focus is dropped, and calls two identical frames a
finding. It walks 236 sandbox stories, the sign-in screens and 51 signed-in
surfaces. Nine defects came out of it, among them a collapsed accordion that
kept its rows focusable and could not be opened from the keyboard at all, a
sidebar search that dropped focus onto `document.body`, a pinned-message unpin
button that was a tab stop underneath another button, and wallpaper tiles whose
ring was painted over by their own media child.

`pnpm test:a11y:app` closes the other blind spot: `test:a11y` runs against an
unauthenticated server, so the chat list, a conversation, the profile sidebar
and settings — most of the product — had never been scanned at all.

Verified: tsc, lint, 3204 unit tests, the browser a11y matrix across Chromium,
Chromium-touch, Firefox and WebKit, Axe over every popup story in four themes
with the contrast correction on, every sandbox story rendering, and the three
focus sweeps at 0 findings.

Not verified, and worth knowing: no screen reader was ever confirmed speaking —
VoiceOver was enabled but its speech panel could not be reached, and NVDA was
not tested. Real calls, with transport and a second participant, were not
exercised either.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
E
Eduard Kuzmenko committed
472e3e76b2dbdbe6b76cc111eaf4638979266e2a
Parent: 4a58f79