Public: anyone can read this; the crew can change it.
The page changes under your hands without losing them: focus kept across a live swap, and a word for a screen reader #70
Sign in to join this conversation.
Public: anyone can read this; the crew can change it.
Intent. Make the live refresh safe for the two people it currently fails — someone using a keyboard, and someone using a screen reader — before the turn engine puts buttons that matter inside those regions.
Measured on
maintoday (e933965):static/live.jsrefetcheslocation.hrefon every event and doesel.replaceWith(next)for each[data-live]region. The lobby's#tablesregion holds the Sit and Leave buttons;/tables/1's#tableregion holds Take a seat. So when anyone joins any table, every open page replaces the element the reader may be standing on: keyboard focus falls back to<body>, an open<select>closes, and a screen reader is told nothing at all — no region on either page carriesaria-live,role="status"or anything else that announces. On a six-hour table this happens while people read, not while they act.Done looks like
id(or the same position among the region's focusables), focus and the text selection are restored after the swap. If it is genuinely gone, focus goes to the region, not to<body>.aria-live="polite"and anaria-label, so a seat filling is heard as well as seen, and the announcement is the region's own text rather than a second copy of it invented for screen readers.prefers-reduced-motionis honoured by whatever highlight the swap gets, if it gets one.static/when.jsalready is: the focus rule comes out oflive.jsas a small exported function (what was focused, what the fresh region holds → what to focus) with a test insrc/that imports it directly, and the server half is asserted as HTML — every[data-live]insrc/page.ts,src/pages/tableview.tsand/stylehas anidand the announcing attributes. Both fail without the change; neither needs a DOM or a dependency.Files I expect to change.
static/live.js,src/page.ts,src/pages/tableview.ts,src/pages/layout.ts,src/pages/style.ts, a newsrc/live.client.test.ts(or a section insrc/server.test.ts),src/pages/style.test.ts,AGENTS.mdwhere it says whatdata-livemeans.Risks. No DOM library — if the pure-function split cannot carry the test, say so in the pull rather than adding one. Do not let the deferral rule mean a region never refreshes: defer, then swap when the control is left.
aria-live="polite"on a region that refetches the whole page can become a firehose once acts arrive (#12) — keep the announced region small and say which one it is. This touchesstatic/live.js, which #43 will also touch; #43 changes when a refresh happens, this changes what a swap does to the reader, and whichever lands second rebases.Who. @mason, reviewed by @juniper. Ready now.