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.
File:
src/components/board/board-display.tsx(also applies tostatic-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
BoardDisplayexposes the board as a singlerole="img"whosearia-labelis 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 — thearia-labelon therole="img"silently changes. A changedaria-labelon a staticrole="img"is not announced by screen readers; only content changes inside anaria-liveregion 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, andSplitFlapAnimationstories 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 thearia-labelmutates, 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(orlive?: "off" | "polite") prop. When enabled, render a visually-hidden<div aria-live="polite" aria-atomic="true">alongside therole="img"that mirrors the resolvedboardText, so a message change is announced once, politely.It must default to off. In editor/preview contexts (
ScaledBoardDisplayin 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(notassertive) 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.