Level AA

Status messages rendered as plain divs without role="status", role="alert", or aria-live are invisible to screen readers -- the text appears on screen but AT has no event to announce.

4.1.3 Status Messages

In Plain Language

4.1.3 Status Messages (Level AA, new in WCAG 2.1) requires that status messages -- success confirmations, errors, waiting states, progress updates -- be programmatically determinable through a role or property, so assistive technology can present them without receiving focus[1].

The mechanism is ARIA live regions. role="status" establishes an implicit aria-live="polite" region; role="alert" establishes an implicit aria-live="assertive" region; role="log" handles chronological append-only updates (chat, terminal); arbitrary containers can use aria-live="polite" or aria-live="assertive" directly.

Why It Matters

  • A "Form saved" toast rendered as a styled <div> with no role or aria-live is visually present and acoustically absent. The DOM mutation fires, the pixels paint, and the screen reader emits nothing -- there is no live region for the AT to watch.
  • A search results counter that updates <span class="result-count">12</span> in place gives sighted users instant feedback and leaves screen reader users with no event: the filter ran, the count changed, the AT never spoke.
  • Inline form-validation errors that appear next to a field but carry no role="alert" force screen reader users to re-traverse the form hunting for what went wrong. The error text exists; the announcement does not.
  • Session-timeout warnings and network-failure notices are the canonical role="alert" case -- they must interrupt current speech because the user cannot act on a warning they never heard.

Examples

Do: Status message with role="status"

✔ role="status" makes the message a polite live region -- screen readers announce it automatically

<button type="button" onclick="addToCart()">Add to Cart</button>
<div role="status"><!-- empty initially --></div>

<!-- After click, JavaScript inserts: -->
<div role="status">Item added to cart</div>
<!-- Screen reader announces: "Item added to cart" -->
Don't: Status message with no live region

✘ No role or aria-live -- screen reader users never hear the confirmation

<button type="button" onclick="addToCart()">Add to Cart</button>
<div id="msg"><!-- empty initially --></div>

<!-- After click, JavaScript inserts: -->
<div id="msg">Item added to cart</div>
<!-- Screen reader announces: NOTHING -->
Do: Urgent alert with role="alert"
Your session will expire in 2 minutes. Save your work now.

✔ role="alert" interrupts the screen reader immediately with the urgent message

<div role="alert">
  Your session will expire in 2 minutes. Save your work now.
</div>
<!-- Screen reader interrupts immediately: -->
<!-- "Your session will expire in 2 minutes. Save your work now." -->
Don't: Moving focus to announce a message
Your session will expire in 2 minutes.

✘ Moving focus to a message disrupts the user's workflow -- use role="alert" instead

<!-- FAILS: moving focus to announce a status message -->
<div tabindex="-1" id="timeout-warning">
  Your session will expire in 2 minutes.
</div>
<script>
  // This yanks focus away from wherever the user was
  document.getElementById('timeout-warning').focus();
</script>
<!-- User loses their place in the form -->

How to Fix It

  1. Use role="status" for non-urgent updates. Success confirmations, result counts, and progress ticks belong in a role="status" container, which resolves to aria-live="polite". The AT queues the announcement and speaks it after the current utterance finishes.
  2. Use role="alert" for errors and time-critical warnings. Validation errors, network failures, and session-timeout warnings belong in role="alert", which resolves to aria-live="assertive". The AT interrupts whatever it was saying and speaks the alert immediately.
  3. Establish the live region before inserting the status text. Assistive technology only watches live regions that exist at the time of the DOM mutation. If you inject the <div role="status"> and its text in the same operation, the region did not exist when AT cached the accessibility tree and the announcement is lost. Render the empty container on page load and mutate only its textContent.
  4. Do not move focus to announce a status message. The entire point of 4.1.3 is that status is presented without receiving focus[1]. Yanking focus to an error message drops the user out of the field they were editing and forces them to navigate back.
  5. Keep the announced string short. Live-region text is read verbatim and cannot be skipped. "3 results" is a status message; a paragraph of marketing copy is a workflow interruption.

References

  1. [1] W3C (2023). Understanding Success Criterion 4.1.3: Status Messages. W3C, Accessed 2026-04-07. https://www.w3.org/WAI/WCAG22/Understanding/status-messages.html