Robust · Level AA · 4.1.3
Status Messages
A message that reports the outcome of something — success, an error, a progress update, a result count — without moving focus or changing what's on screen in a bigger way, has to be exposed to assistive technology the moment it appears, not just shown visually.
- Blind
- Low vision
- Cognitive
How to recognize it
Trigger whatever produces a transient message on the page — submit a form with bad data, add something to a cart, run a search, wait for a background process — with a screen reader running, and check whether anything actually gets announced. The common failure: a toast or snackbar ('Item added to cart') that appears and fades on screen but was just inserted as a plain element with no live region, so it's entirely silent unless the user happens to be navigating through that exact part of the page at that exact moment. A form validation summary ('2 errors found') that appears above the form but isn't announced when it shows up — the only way to discover it is to already know to go check. A results count ('14 results') that updates via JavaScript with no live region while the list itself re-renders below it. Not every dynamic change qualifies, though: a modal dialog opening isn't a status message (that's a focus-and-naming concern, not an announcement one), and an accordion expanding isn't either — those have their own mechanisms.
How to fix it
Wrap the message in the right live region role: role="status" (implicitly aria-live="polite") for routine updates like success confirmations, results counts, or progress; role="alert" (implicitly aria-live="assertive") for errors that need more immediate attention. The live region has to already exist in the DOM before the message appears — inserting a brand-new element with role="status" and content at the same instant is unreliable across screen readers. Keep an empty live region present from page load and update its text content when there's something to announce, rather than creating and destroying the element each time. And don't move focus to the message: that's not required, and it usually makes the experience worse by yanking someone away from what they were doing — the whole point of this criterion is that the message gets announced passively while the user stays put. That advice is specifically for passive notifications, though (a toast, a results count) — it doesn't apply the same way to a blocking form-submission error that stops the user from proceeding at all. For that case, WAI's own forms guidance recommends moving focus to the first field with an error, which is really 3.3.1's territory rather than this criterion's: a passive role="alert" announcement is audio-only, so it does nothing for a low-vision user at high zoom who isn't running a screen reader and whose viewport is scrolled somewhere else entirely. Moving focus solves that too, as a side effect — the browser scrolls the focused field into view regardless of where the page currently is — and placing the error text directly next to the field it describes helps the same way even without moving focus.
Example
Avoid
function addToCart() {
const toast = document.createElement('div');
toast.textContent = 'Item added to cart';
document.body.appendChild(toast);
}<div id="search-count"></div>
<!-- updated via searchCount.textContent = '14 results', no live region --><div id="form-errors"></div>
<!-- populated with error text on submit, never announced -->Prefer
<div role="status" aria-live="polite" id="toast"></div>
<!-- present from page load; toast.textContent = 'Item added to cart' on the event --><div id="search-count" role="status" aria-live="polite"></div><div id="form-errors" role="alert" aria-live="assertive"></div>