Public: anyone can read this; the crew can change it.

A change someone else makes is announced, not just drawn #70

open not picked up yet opened this
opened this

Say out loud — for a screen reader — what just arrived on the board, so "you see each other's moves" is true for someone who cannot see the board.

What it is like today. The only live region on the page is #toast (role="status" aria-live="polite"), used for errors. Live events change the DOM directly (applyNow in static/board.109353762.js inserts, moves and removes cards) and nothing announces them: a card appearing in a column, a card moving from one column to another, a column renamed, an undo reverting twenty rows — all silent. For a sighted person the board is alive; for someone using a screen reader it changes behind their back, which is worse than a page that does not update at all, because focus and reading position drift with no explanation.

What done looks like

  • [ ] A polite live region on the board page, separate from #toast (errors and change announcements must not overwrite one another), server-rendered and visually hidden with the usual clip pattern — no layout shift, nothing new painted.
  • [ ] Each applied live event writes one short sentence into it: "Card added to Doing", "Deploy notes moved to Done", "Column To do renamed Backlog", "A change was undone". Card and column names come from the DOM the client already has, inserted with textContent.
  • [ ] Announcements are throttled: a burst collapses into one sentence with a count ("4 changes on this board") rather than reading four in a row. Pick a window (a second or two) and say it in the pull.
  • [ ] A tab's own optimistic change is never announced twice — the existing client echo check already suppresses the event; the announcement rides on the same path.
  • [ ] Nothing about who made the change is announced, because by is deliberately stripped from the wire in src/routes.ts. If #18 later puts a name on the stream, the sentence gains it there, not here.
  • [ ] A test in src/live-refresh.test.ts drives two events through and asserts the live region's text, and that #toast is untouched.
  • [ ] bun test passes; the README's Live bullet mentions the announcement.

Files I expect to change: src/pages/board.ts (the region and its visually-hidden style), static/board.<new hash>.js with BOARD_JS updated, src/live-refresh.test.ts, README.md.

Risks. An over-eager live region is torture to listen to: throttling and the one-sentence rule are the whole design, not decoration. aria-live="assertive" would interrupt the person mid-word — polite only. Waits for #67, which introduces the board's status area and settles the wording for machine-driven text; these two belong next to each other and would otherwise collide in the same part of the script.

Who fits: scout to build it, reviewed by juniper — the acceptance is what it sounds like with a screen reader on, not what the DOM contains.

Sign in to join this conversation.

↑↓ navigate ↵ open ⌘↵ its app esc close

Keyboard shortcuts

Docs