Status Messages
Status messages — confirmations, errors, progress updates, search results counts — must be announced to assistive technology without moving focus. Use role=status, role=alert, or aria-live on a region that already exists in the DOM.
What it asks
When the page tells the user something happened — “Item added to cart,” “3 results found,” “Connection lost,” “Saving…” — that message must reach screen-reader users without forcing focus to move. The information has to be programmatically determined as a status, so assistive technology can pick it up and announce it.
Three categories WCAG calls out:
- Success / completion — “Profile saved,” “Email sent.”
- Results of an action — “12 results,” “No matches found.”
- State of the application — “Saving…”, “Reconnecting,” “Upload 40%.”
How to meet it
- Put a live region in the DOM at page load — an empty element with
role="status"(polite) orrole="alert"(assertive). On update, write the new text into it. - For non-critical confirmations and results counts, use
role="status"oraria-live="polite". Screen readers wait for the user to be idle, then announce. - For critical messages — form-submission errors, lost connection, session about to expire — use
role="alert"oraria-live="assertive". The announcement interrupts. - Toast notifications need a live region. The toast container should exist in the DOM before the toast appears, with
aria-liveset, and the toast text injected as a child. - Pair
aria-livewitharia-atomic="true"if you want the whole region re-read on every update, not just the changed nodes. - For form errors, the inline error message can use
role="alert", or move focus to a summary at the top of the form — but don’t do both; users only need to hear it once.
Common failures
- Toast notifications rendered into an element that didn’t exist before the toast — the live-region listener never attaches, nothing is announced.
- A success banner that gets inserted but lives outside any
aria-liveregion — visible to sighted users, invisible to screen-reader users. - Search results count (“Showing 12 of 340”) that updates silently after filtering. Users tab back into the results unsure if anything changed.
- Form validation that updates an inline
<span class="error">with norole="alert"oraria-live— error appears, screen reader stays silent. - Using
aria-live="assertive"for every status update. Constant interruptions; screen-reader users disable the page. - Moving focus to a status message that’s not a heading or interactive — focus lands on a non-tabbable element, keyboard users lose their place.
- “Saving…” / “Saved” indicators that flip a CSS class but never update any text content inside a live region.
Why it matters
Status messages are where modern single-page apps fall over. The page never reloads, focus rarely moves, and so the user’s only signal that something happened is a visual change — useless to anyone not looking at the screen. 4.1.3 forces parity: if a sighted user can see “Added to cart,” a screen-reader user can hear it. The fix is almost always cheap (one live region, populated on update) and the absence of it almost always breaks core flows — checkout, search, form submission, real-time chat.