Skip to content

a11y: board-display.tsx — message updates are silent to screen readers (no live-region option) #206

Description

@claude

File: src/components/board/board-display.tsx (also applies to static-board-display.tsx)

Category: B (missing capability — no live-region option for silent content updates)

WCAG: 4.1.3 Status Messages (AA); related 1.3.1 Info and Relationships.

The barrier

BoardDisplay exposes the board as a single role="img" whose aria-label is recomputed whenever the message changes (board-display.tsx:1320–1329, :1341–1342). When the board's message updates in place — the component's namesake use case, a live message board — the aria-label on the role="img" silently changes. A changed aria-label on a static role="img" is not announced by screen readers; only content changes inside an aria-live region are. There is no live region anywhere in the component, and no prop to opt into one.

So a message swap is completely silent to assistive tech. The CyclingMessages, LoadingTransition, MessageTransition, and SplitFlapAnimation stories all mutate the message at runtime; a sighted user watches the board flip to new content, a screen-reader user hears nothing and has no way to know the board changed.

Who is affected, and how

A blind user with a FiestaBoard display mirrored on screen (weather, transit times, alerts updating live) gets no notification when the board changes — the very information the board exists to convey arrives silently. They would have to manually re-navigate to the role="img" and re-read its label on the chance it changed. For a genuinely live board this is the difference between "the 8:15 bus is now 2 min away" being announced and being invisible.

Why existing tooling misses it

axe inspects a static DOM snapshot. It sees a valid, well-named role="img" and passes. It cannot press play on the cycling stories, cannot observe that the aria-label mutates, and has no rule for "this content updates but is not in a live region." This is squarely in axe's blind spot.

Suggested fix

Add an opt-in live-region capability — e.g. an announceUpdates?: boolean (or live?: "off" | "polite") prop. When enabled, render a visually-hidden <div aria-live="polite" aria-atomic="true"> alongside the role="img" that mirrors the resolved boardText, so a message change is announced once, politely.

It must default to off. In editor/preview contexts (ScaledBoardDisplay in the page editor, static thumbnails) the message changes on every keystroke, and a live region there would be intolerably chatty — announce-on-update is only correct for a genuinely live display, which only the consuming app knows. polite (not assertive) is right: a board update is informational, not urgent.

Visually neutral — the live region is sr-only, adds no rendered pixels, and defaults off, so existing stories render identically. No VRT baseline reseed required.


Filed by the a11y-audit cron — round 1.

Metadata

Metadata

Assignees

No one assigned

    Labels

    a11yAccessibility: keyboard, screen reader, motion, contrasta11y-auditFiled by the a11y-audit cronclaude-fixRun the Claude issue-triage workflow on this issue

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions