Skip to content

Chat responses have no ARIA live region — new messages are never announced to screen readers #7081

Description

@JSONbored

Context

The chat rail's message rendering path — apps/loopover-miner-ui/src/components/chat/message-list.tsx
(the <ol> of MessageBubble rows), apps/loopover-miner-ui/src/components/chat/message-bubble.tsx,
and apps/loopover-miner-ui/src/components/streaming-text.tsx (the live-streaming answer) — has no
aria-live region anywhere in its normal, non-loading/non-error rendering path. A grep across all
four chat-rendering files for aria-live/role= finds matches only inside
@loopover/ui-kit's StateBoundary (used for the loading/error/empty branches) and in
typing-indicator.tsx (which, per a separate issue in this batch, is not currently wired up either).
The actual message content — both the final committed MessageBubble entries and the live
StreamingText answer as it accumulates — is rendered into plain, non-live DOM.

Because there is no live region, a screen-reader user who submits a chat question and then moves
focus elsewhere (as the composer's own accessible flow encourages, since ChatComposer does not trap
focus) receives no announcement when the assistant's answer starts streaming in, updates token by
token, or finishes. The response is only discoverable by manually re-navigating into the message
list. This is a genuine WCAG-relevant gap for a live, streaming chat surface, distinct from — but
adjacent to — the composing-indicator gap filed separately in this batch.

Requirements

  • The region containing new assistant/system messages (or, at minimum, the newly-appended message
    once a turn completes) must be an ARIA live region (aria-live="polite" is appropriate here — chat
    answers are not urgent/interrupting content) so assistive tech announces new content without
    requiring the user to have focus inside the message list.
  • The live announcement must fire once per completed turn, not on every incremental streaming chunk —
    announcing dozens of partial-token updates would be worse than the current silence (a classic
    "too chatty" live-region regression). StreamingText's per-chunk re-renders must not each trigger
    a fresh announcement.
  • The existing StateBoundary-driven loading/empty/error announcements (already role="status"/"alert")
    must be unaffected by this change.

Deliverables

  • New assistant/system messages are announced to assistive tech once a turn completes
  • Incremental streaming chunks do not each trigger a separate announcement
  • Regression test asserting the message-list container (or an equivalent live region) carries aria-live and that its accessible content updates once per completed turn

Test Coverage Requirements

apps/** is listed under ignore: in codecov.yml, so this app is not gated by Codecov's 99% patch
check — but the local npm run test:coverage invariant bar still applies, and the regression test
above is required. This is an accessibility/attribute change with no visual layout impact, so no
before/after screenshot is required.

Expected Outcome

A screen-reader user gets a single, well-timed announcement per completed chat turn — matching the
sighted experience of watching the answer arrive — without being spammed by per-token announcements.

Links & Resources

apps/loopover-miner-ui/src/components/chat/message-list.tsx,
apps/loopover-miner-ui/src/components/chat/message-bubble.tsx,
apps/loopover-miner-ui/src/components/streaming-text.tsx,
apps/loopover-miner-ui/src/components/chat/conversation.tsx.

Metadata

Metadata

Assignees

No one assigned

    Labels

    gittensor:bugGittensor-scored bug fix — scores a 0.05x multiplier.help wantedExtra attention is needed

    Projects

    No projects

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions