How to build an accessible AiStatus in React
The label lives in a role="status" node holding one stable string per phase, so each phase change is announced once and token-level streaming never is. The glyph is aria-hidden and only it pulses (under prefers-reduced-motion: no-preference), so the label never drops below contrast. Stop is a real button outside the status node, so pressing it does not re-announce; forced colours render everything CanvasText
When to use a AiStatus
- Telling the reader an AI request is in flight, and which phase it is in (thinking before output, generating while it streams)
- Offering a keyboard-reachable Stop while a response is being produced
- Reporting the terminal outcome of a generation — done, failed or stopped
When not to use it
- Non-AI async work such as saving a form — use InlineLoading
- Holding the place of a chat reply with no text — use TypingIndicator
- Determinate progress with a known percentage — use ProgressBar
Keyboard interactions
Role status, verified at WCAG 2.2-AA.
TabEnterSpace
Common mistakes
Avoid: aria-live on the streaming response text
Prefer: <AiStatus status={phase} /> beside the response, which renders silently
A live region on streaming text re-announces on every token; AiStatus announces each phase change once
Avoid: Showing AiStatus for a response that arrives in under a second
Prefer: Delay mounting it ~1s so fast responses never flash a loader
A loader that blinks on and off reads as a glitch and makes a screen reader announce noise
Example
<AiStatus status="thinking" />