Skip to main content
WCAGrules
Quick navigation

Guides · Interface patterns

Notifications That Reach the People Who Cannot See Them

A message that appears in the corner and fades after four seconds was written for one kind of user.

Last reviewed August 31, 2026

Saved. Added to basket. Three errors found. These are the small confirmations an interface uses to tell somebody their action worked, and they are drawn as far from where the person was looking as the layout allows, then taken away again after a few seconds.

A sighted user catches that in peripheral vision. A screen reader user hears nothing at all unless the message was marked as something to announce. Somebody working at high magnification is looking at a fraction of the screen and misses a corner toast as completely as if it never rendered.

One thing to settle before the ticket is written. The Authoring Practices Guide covers thirty component patterns and the toast is not one of them. The Alert pattern is the nearest thing in it and it is a different animal, because an alert is a single live region rather than a stack of dismissible messages on a timer. So there is no canonical keyboard model here to copy, and the rules below are the whole of what you are held to.

What the Rule Requires

4.1.3 Status Messages says that when a page tells somebody something without moving focus there, assistive technology has to be able to announce it. Note what it does not say. It does not require you to invent messages. A page that shows no status messages passes it without doing anything, and the whole criterion is about the ones you already display reaching everybody you already display them to.

The definition is narrower than anything that appears. A status message reports the success or result of an action, a waiting state, the progress of a process, or the existence of errors, and it is not a change of context. New form fields appearing on the page are not status messages, and neither is a list of search results. The count above the list is.

Moving focus to the message instead is a different design rather than a forbidden one. It takes the change out of this criterion entirely, because a change of context is not a status message, and it can raise its own problems under the rules about things happening on focus or on input. It is usually the wrong choice for a routine confirmation, because interrupting somebody's task to tell them the thing they expected happened is worse than telling them quietly. Pick one route. Doing both announces it twice.

The live region has to exist on the page before the message goes into it. Insert the container and its text in the same instant and assistive technology tends to announce nothing, because it was not watching a region that did not exist a moment ago. Start with an empty live region, then write into it as a separate step. There is one documented exception, which is role="alert", where content is usually announced even when the region arrives with its message already inside. Treat that as a convenience rather than something to design around, because the two most-read sources hedge it differently and one of them says outright that alerts present before the page finishes loading are not announced at all.

That last point has a practical consequence worth carrying. A server-rendered error summary on a reloaded page, which is the most common implementation of a good pattern, may be silent for exactly that reason.

Polite or Assertive

SettingBehaviorUse for
aria-live="polite"Queues, and waits for a pause in speechSaved, added, results updated
aria-live="assertive"Interrupts whatever is being saidErrors, session expiry, payment failed
role="status"A named role that is polite by default, and atomic by defaultMost confirmations
role="alert"A named role that is assertive by default, and atomic by defaultGenuine problems needing attention now
Choosing the politeness level

Five things can carry a status message, and a tester who knows three of them files false findings. The HTML output element, role="status", role="alert", role="log", and aria-live set to polite or assertive. The absence of all five is the documented failure, and role="status" is the published technique for a routine confirmation.

Reach for polite by default. An assertive announcement cuts off whatever the screen reader was in the middle of saying, so it is a real cost, and it should be spent on the errors and the delays that cost the user something. One implementation detail with a name. Combining aria-live and role="alert" on the same element is a common belt-and-braces habit and it causes double speaking in VoiceOver on iOS, so pick one. And in some browser and screen reader pairs an alert is automatically prefixed with the word Alert when it is read, which is worth knowing before you write Alert into the message text as well.

role="log" is the one nobody reaches for and it is the right answer more often than you would think. A log region appends rather than replacing, so it suits anything where entries accumulate and the older ones stay relevant, such as a chat thread or a run of file uploads finishing one after another.

The Attribute Nobody Sets

Politeness decides when a region speaks. aria-atomic decides what it says. Set to true, the whole region is read out when anything inside it changes. Set to false, only the part that changed is read.

That matters more than it sounds. A basket badge going from 2 to 3 inside a sentence announces the word three, and nothing else, because three is the only thing that changed. role="status" carries an implicit atomic value of true and a bare aria-live="polite" container defaults to false, so the two are not interchangeable however similar they look. W3C's own technique says to set aria-atomic="true" explicitly anyway, because some environments do not treat role="status" as atomic by default. One attribute turns three into three items in your basket.

There is a companion attribute worth knowing about, because it decides something the first two do not. aria-relevant controls which kinds of change count, and it defaults to additions and text. Removals are not announced. That is the mechanism behind a real problem the next section is about.

One argument for keeping status text short that has nothing to do with speech. Assistive technology may reserve a few cells of a braille display to render a status region, and a sentence does not fit in a few cells.

A Message Disappearing Is Also Information

This is the case almost nobody handles. A Saving indicator appears, then vanishes when the save completes. For a sighted user the vanishing is the news. For a screen reader user nothing was announced, because removals are not announced by default, so the last thing they heard was Saving and there has been silence ever since.

W3C's answer is to change the visible text rather than remove it, so Saving becomes Saved. Where the design will not allow that, put the words into a visually hidden status region instead, so something says the state ended. The same applies to a maintenance banner that simply disappears when the service comes back.

Toasts and the Clock

Four seconds is a common default in component libraries. It is enough time to glance at a message and not much else, and it is not enough for somebody magnifying the screen who has to find the toast before reading it, or somebody whose screen reader is mid-announcement when it appears, or anybody who needs to read a sentence twice.

Here is the part we had wrong, and it changes what you should build. A dismiss control is not what 2.2.1 Timing Adjustable asks for. That criterion names three routes and a dismiss button is none of them. Turn the limit off before it is encountered. Adjust it, over a range of at least ten times the default, before it is encountered. Or warn the user before it expires, give them at least 20 seconds to extend it with a simple action, and let them extend at least ten times. Pausing on hover is not one of them either, and it cannot be, because a screen reader user is not hovering.

What actually decides conformance is a different question altogether, and W3C works a toast through as its own example. An email client shows a new-mail toast that disappears after five seconds. That conforms, because the arrival of the mail is discoverable in the inbox anyway, so the disappearing message set no time limit on anybody's ability to find out. Then comes the sentence that matters. If the user has no other means of discovering the same information, each message has to meet the criterion in full.

So the test is not how many seconds. It is whether the information exists anywhere else.

  • Never put the only copy of important information in something that disappears. A failed payment needs a message that stays. This is the test, not a nicety.
  • Where the same news is recoverable elsewhere, a short-lived toast is fine, and a longer duration with a dismiss control is still kinder.
  • Where it is not recoverable elsewhere, you owe one of the three routes above, or you remove the timer.
  • Think before you stack them. Preserving every toast so a fast sequence does not overwrite itself solves one problem and walks into another, because 2.2.4 Interruptions is about letting somebody postpone or turn off interruptions, and frequent interruption is specifically named as harmful for people with visual and cognitive disabilities. A queue of assertive toasts is the thing that criterion describes.
  • At Level AAA the argument is over. 2.2.3 No Timing removes time limits from anything that is not a live event, so an alert that disappears on its own fails outright there rather than conditionally.

Test it with the sound on

Turn on a screen reader, trigger the action, and listen. Markup can look correct and announce nothing at all, so this is the only check that settles it. Two cautions before you call it done. Some of the behaviors here are specific to one browser and screen reader pair, including the automatic Alert prefix, the double speaking when aria-live and role="alert" are combined, and the silence on a page that loaded with its alert already in place. One pass with one screen reader will not find all three.

Where the Message Should Live

Next to whatever it refers to. A form error beside its field. A confirmation where the action happened. The corner of the viewport is a layout convenience, not a reading position, and it is the furthest point on the page from where the person was looking.

For form errors specifically, a summary at the top that links to each broken field serves everybody, and our forms guide covers the shape of it. Remember which route you took, though. If that summary takes focus, it needs no live region around it and adding one says everything twice.

Common questions

Should I use role=alert for form validation errors?
For errors after a submission, yes, because that is genuinely time-sensitive and the user is waiting on it. For inline validation as somebody types, use polite instead. Assertive interrupts whatever the screen reader was saying, and W3C lists using an alert on content that is not important and time-sensitive as a thing to avoid.
Why does my live region announce nothing?
Usually because the container and the message arrived at the same moment. Assistive technology tends to watch regions that were already on the page, so put an empty region in the markup and write into it as a separate step. That is true of aria-live and role="status". role="alert" is the documented exception and is usually announced either way, with its own caveat that an alert already present when the page loads may not be announced at all.
Does a toast that disappears after four seconds fail WCAG?
It depends on whether the information is anywhere else. W3C's own worked example is a five-second toast that conforms, because the same news is in the inbox. Where the message is the only copy, the timing rule applies in full, and its three routes are turning the limit off, adjusting it over a wide range, or warning the user and letting them extend it. A dismiss button is none of those, and neither is pausing on hover.
What is aria-atomic for?
It decides how much of a live region gets read when part of it changes. Set to true the whole region is announced, so a basket counter says three items in your basket. Set to false only the changed part is announced, so the same counter says three. role="status" is atomic by default and a bare aria-live="polite" container is not, and W3C's own technique advises setting the attribute explicitly because some environments do not honor the implicit value.
How do I announce that something finished?
Change the visible text rather than removing it, so Saving becomes Saved. Removals are not announced by default, because the attribute controlling which changes count defaults to additions and text. Where you cannot change the visible words, put the ending into a visually hidden status region so somebody hears that the state is over.

Sources

Keep reading

More on interface patterns

Reading about it is the cheap part.

Find out where your site actually stands. The free scan checks 10 pages in a real browser against all 90 supported automated rules, keeps its 27 best-practice checks separate from WCAG findings, and names the rule behind every finding. The full audit adds an expert review and a real blind screen-reader user. From $499, with the report in 5 business days on Rapid and 10 on Standard, and the clock starting at cleared payment.

Go somewhere useful

Find tools, resources and your workspace.

29 destinations