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