4.1.3 Status Messages
- Level AA
- Robust
- Since WCAG 2.1
Summary
When a page tells a sighted user something changed (a toast saying "item added to basket", a filter panel updating to "3 results found", a quiet "all changes saved" confirmation, a spinner replaced by loaded content), a screen reader user gets none of it unless the message is exposed programmatically. This criterion, added at Level AA in WCAG 2.1, requires that such status messages be announced by assistive technology without moving keyboard focus. A status message the user has to discover by hunting around the page is, in practice, invisible to a screen reader user: they carry on working with no idea the update ever happened.
The auditor's mental model has two gates. First, is it a status message: text that appears (or changes) to report the outcome or progress of something, without a change of context? Second, does it receive focus? This criterion covers only messages that do NOT take focus. A modal error dialog that seizes focus is out of scope here (the dialog itself is graded under other criteria); the classic in-scope cases are the silent, in-place updates. The European standard applies the same requirement to web content and to software that supports assistive technologies; on systems closed to assistive technology it is not applicable.
Official wording
In content implemented using markup languages, status messages can be programmatically determined through role or properties such that they can be presented to the user by assistive technologies without receiving focus.
EN 301 549 mapping
- Web pages
- Clause 9.4.1.3
- Software and native apps
- Clause 11.4.1.3
- Closed functionality (kiosks, terminals)
Not applicable on closed systems.
Clause 9.4.1.3 applies this criterion to web pages unchanged and clause 11.4.1.3 applies it to software that supports assistive technologies. On systems closed to assistive technology the requirement is not applicable.
Clause references are to EN 301 549 V3.2.1 (2021-03), the harmonised European standard. Descriptions are our own summary, not the text of the standard.
In practice
Web
Test with a screen reader running (VoiceOver with Safari, or NVDA with a Chromium browser) and trigger every status-producing action on the screen: add an item to a basket, submit a form that saves asynchronously, apply a search filter, upload a file, let a loading state resolve. Listen. Each visible status update should be announced automatically, without you touching the keyboard and without focus moving. Silence at the moment the toast appears is the failure.
Then inspect the markup in browser devtools. Statuses are exposed through live regions: role="status" or aria-live="polite" for advisory updates, role="alert" or aria-live="assertive" for urgent ones, role="progressbar" or role="log" where those semantics fit. The classic implementation trap: the live region container must exist in the DOM BEFORE the message text is injected into it. A <div role="status"> inserted complete with its text in one step is frequently not announced, because assistive technology never registered the region to watch. Check whether the empty container is present on page load (or well before the update) and only its contents change.
Common web failure spots: toast libraries rendered without a live role, results counts that update as you type into a filter with no aria-live, "saved" ticks that appear purely visually, and carts that update a badge number with no announcement. Also grade over-announcement judgement: aria-live="assertive" on a frequently updating region is not a 4.1.3 fail (the message is programmatically determinable), but note it as a quality issue.
iOS
Run VoiceOver and trigger every toast, banner, auto-save confirmation, and results-count update in the app. Each one should be spoken as it appears, with the VoiceOver cursor staying where it was. iOS has no native toast component, so teams build their own snackbar equivalents, and in practice these are the number one failure: a custom overlay view animates in and out in complete silence.
The correct implementations to look for are UIAccessibility announcement notifications (posting an announcement notification with the message text in UIKit) and, in SwiftUI, AccessibilityNotification.Announcement. You can confirm the mechanism in code review or by observing behaviour: if the message is spoken without the VoiceOver focus moving, an announcement (or an equivalent posted notification) is being used. Use Accessibility Inspector on the overlay itself as a secondary check, but remember that an element merely being present in the accessibility tree is not enough; the user is never told to go looking for it, so it must be announced at the moment it appears.
Also verify timing and interruption: an announcement fired before the app finishes another spoken update can be swallowed. Trigger each status two or three times to check it announces reliably, not just once.
Android
Run TalkBack and trigger the same set of actions. Material Snackbar announces its text by default through an accessibility event, but verify rather than assume: custom snackbar styling, custom toast-like overlays, and snackbars shown from unusual window contexts can end up silent. Framework Toast also normally announces, but apps that draw their own toast lookalikes on a decor view usually do not.
For in-place updates (results counts, "saved" labels, progress text), look for live region configuration: android:accessibilityLiveRegion="polite" (or assertive) on the view whose text changes, set in XML or via setAccessibilityLiveRegion. In Jetpack Compose, the equivalent is Modifier.semantics { liveRegion = LiveRegionMode.Polite } on the composable holding the status text. With a live region set, changing the text triggers the announcement automatically; with nothing set, the text changes silently and the criterion fails. One-off announcements fired through accessibility events are also acceptable where a live region does not fit the component's lifecycle.
Common Android failure spots: cart badges that increment silently, pull-to-refresh completing with no spoken confirmation, form screens whose inline "saving..." then "saved" text has no live region, and Compose screens where the team assumed the View-world Snackbar behaviour carried over to a hand-rolled composable.
Pass and fail examples
Passes:
- An "item added to basket" toast rendered into a
role="status"container that was present, empty, in the DOM from page load: announced politely, focus does not move. - A search filter that updates a "3 results found" line inside an
aria-live="polite"region as facets are toggled. - An iOS document editor that posts a UIAccessibility announcement of "all changes saved" when its auto-save completes, while VoiceOver focus stays in the text field.
- A Material Snackbar confirming "message archived", verified with TalkBack to announce on appearance.
- A long-running upload whose completion replaces a spinner and fires a polite "upload complete" announcement.
Fails:
- A toast library that animates a confirmation into view with no live region role: visible to sighted users, silent for screen reader users.
- A live region
divinjected into the DOM at the same moment as its message text, so assistive technology never announces it, even though the markup looks correct in a static inspection. - A results count that re-renders as the user types into a filter field, with no
aria-live,role="status", or equivalent. - A custom iOS snackbar equivalent that appears and auto-dismisses without posting an announcement notification.
- A Compose "saved" indicator whose text changes with no
liveRegionsemantics and no accessibility event.
Not a fail under this criterion:
- A modal error dialog that opens and takes focus: it received focus, so it is not a status message under this criterion. Grade the dialog's own semantics under other criteria instead.
- Error messaging that is missing entirely: the absence of any error text is SC 3.3.1 Error Identification; 4.1.3 only grades whether an existing message that does not take focus is announced.
- A control announcing its own new state when operated (an accordion reporting "expanded", a toggle reporting "on"): that is the control's state under SC 4.1.2 Name, Role, Value, not a status message.
- A page that navigates or moves focus when an input changes: a change of context is graded under SC 3.2.2 On Input, and content presented via a change of context is explicitly outside this criterion's definition of a status message.
Commonly confused with
- SC 4.1.2 Name, Role, Value. State changes of the control the user is operating (pressed, expanded, selected, checked) belong to 4.1.2, because the user's focus is on that control and its state is part of its programmatic interface. 4.1.3 covers messages about something ELSE on the page changing while focus stays put. "The toggle didn't say it turned on" is 4.1.2; "the page didn't say the list refreshed" is 4.1.3.
- SC 3.3.1 Error Identification. 3.3.1 asks whether an error is identified and described in text at all; 4.1.3 asks whether a message that appears without taking focus is announced. Inline validation errors often earn one finding under each: text missing is 3.3.1, text present but silent is 4.1.3. File the evidence under the gate that actually failed.
- SC 3.2.2 On Input. Both criteria meet at form controls that react as the user types or toggles. If the reaction is a disruptive change of context (navigation, focus jump, window change) without warning, grade it under 3.2.2. If the reaction is a quiet in-place update that nobody announced, grade it here. The same filter widget can fail either, but never file one behaviour under both.
- Focus-taking dialogs and confirmations. Because the criterion is explicitly limited to messages received WITHOUT focus, moving focus to a message is a legitimate design alternative, not a 4.1.3 fail. Whether that focus move was itself appropriate is a separate question (context change and focus order criteria), so redirect the finding rather than forcing it in here.
How AUDITSU tests this
AUDITSU's audit walkthrough covers status messages on every screen that produces feedback: it prompts you to list the screen's status-producing actions (add to basket, save, filter, upload, refresh), perform each one with the screen reader running, and record whether the update was announced without focus moving. On web screens the walkthrough also directs you to inspect the live region markup and confirm the container exists before the message is injected, which catches the fails that a listen-only test can miss on a lucky first run.
Each check records a pass, fail, or not applicable result per screen with evidence attached (what you triggered, what was and was not announced), so silent toasts and unannounced result counts land in your report tied to the exact screen and action. For the full guided workflow, see the audit platform.