Skip to main content
A Singapore Government Agency Website
A11Y-11L1 · mandatoryhybridAccessibility

Async state changes are announced and focus is managed — transient status via live regions without stealing focus; context replacements move focus to the revealed surface, never both for the same message

Fails when: message announced by both a live region and a focus move · transient toast stealing focus

Requirement

Every async state change communicates through exactly one of two channels, chosen by what the change is:

  • Transient status (saving indicator, success toast, background update): a live region (role="status" / aria-live) that announces without moving focus.
  • Context replacement (a panel replacing the acting surface, an error that needs action): move keyboard focus to the revealed surface or its first actionable element. The focused element's content is voiced by the focus move itself — do NOT also mark it role="alert", or the same message announces twice.

Never both channels for the same message; never neither.

Rationale

Merges the accessibility checklist AA "Status messages" (announceable without focus) with the focus-movement gap caught twice in loop runs 1–2 (post-submit focus stranded on a hidden control; then a banner with role="alert" + programmatic focus announcing twice). The two failures are one design decision made badly: which channel does this state change use? This control forces the choice at plan time — CMP-3's state enumeration is where each state declares its channel.

Passes when

  • Toast "Attendance submitted." is role="status", auto-dismissing, focus untouched.
  • On submit success that hides the acting button, focus moves to the revealed panel's first actionable element.
  • An error banner that requires action receives focus (tabindex="-1" + .focus()) and carries NO role="alert" — the focus move is the announcement.
  • A background auto-save indicator updates a polite live region only.

Fails when

  • Focus rests on a hidden/removed control after a state change (stranded focus).
  • The same message is announced twice (live-region role AND programmatic focus).
  • A state change is visually obvious but silent to AT (no live region, no focus).
  • Focus is yanked for a transient message the user didn't act on (focus theft).

How to verify

Deterministic half — planned checks/async-states extension: for each state surface, assert (live-region role XOR focus-target wiring). Until built: verify manually via DOM inspection (document.activeElement after each transition) and label it so. Judgment half — the evaluator grades the channel choice per state: transient vs context-replacement, and quotes any double-channel message.

Also available as Markdown · catalog.yaml