Accessibility · Assistive Technology
Live Regions and Asynchronous Announcements
How aria-live regions cause screen readers to announce content that changes without focus moving, and how the polite and assertive politeness levels are implemented.
Most of what a screen reader announces is driven by focus: the user moves focus to an element, and the reader announces it. But much of what changes on a screen does so without focus moving. A chat message arrives, a form submits and shows an error, a search returns results. The sighted user sees these changes in peripheral vision; the screen reader user, whose attention is wherever the speech cursor is, sees nothing unless the change itself causes an announcement. The mechanism that turns a DOM mutation into speech is the live region.
The live region as a declaration
A live region is a part of the document that the author has marked as announcement-worthy: content inside it is watched, and when it changes, the change is announced even though focus has not moved there. The marking is the aria-live attribute, whose value is a politeness level, conventionally off, polite, or assertive. The attribute does nothing by itself; it is a signal to the browser and the assistive technology about how urgently the enclosed changes should be spoken.
The off value is the default for content in general and disables announcement. The polite value means the change should be announced when the current utterance finishes, so it never interrupts what the user is already hearing. The assertive value means the change should interrupt the current speech immediately. The politeness level is therefore a scheduling policy: it tells the reader where in its announcement queue the new content goes, and it is the developer’s main tool for expressing priority among simultaneous announcements.
The aria-atomic attribute refines this. When a live region changes, the reader must decide whether to announce only the specific nodes that changed or the whole region’s text. With aria-atomic="true" the reader announces the entire region as a unit; by default it announces only the changed nodes. The distinction matters when a region is a sentence or a status line whose parts only make sense together.
How the change is detected
The browser does not watch live regions by polling their text. It watches the DOM for mutations, and the live region is a filter applied to the mutation stream. When content is added, removed, or changed inside a live region, the mutation carries enough information to identify the region and the changed nodes, and the browser emits an accessibility event describing the change.
The underlying primitive is the MutationObserver mechanism from the DOM specification. The accessibility layer is effectively a consumer of the same mutation stream that a script could observe, and the live-region logic sits on top of it: a mutation whose target or an ancestor carries aria-live with a non-off value produces a live-region event, and a mutation elsewhere does not. This is why dynamically inserted live regions are a known pitfall. If a script creates a live region and immediately populates it in the same task, the content may be present before the region is marked, and the browser can miss the announcement or announce it as a static insert. The reliable pattern is to create the region first, mark it live, and populate it in a later task, so the mutation that adds the content is observed while the region is already live.
A complementary property of the same mechanism is that only additions and text changes announce; removals generally do not. Deleting a node inside a live region is a mutation the observer sees, but there is no text to speak for content that no longer exists, so readers do not read it back. An interface that clears a status message by emptying the region therefore produces silence, which is usually the desired outcome, while an interface that replaces the message produces a second announcement. The two ways of clearing a region are not equivalent, and the choice between them is audible.
The role="status" encoding carries a second, less obvious default: the specification gives the role an implicit aria-live="polite" and aria-atomic="true", so a status region announces its full text as a unit without any further attributes. role="alert" likewise implies aria-live="assertive", and it also implies aria-atomic="true". An author who sets role="status" and separately writes aria-live="polite" has changed nothing; an author who sets role="status" and aria-live="assertive" has mixed the semantics of the two roles, and the explicit attribute wins over the role’s implicit value.
The reader, for its part, does not need to know about MutationObserver. It receives a platform event carrying the region’s identity and the changed text, and it schedules that text according to the region’s politeness. The filtering and the policy are split: the browser does the filtering, the reader does the scheduling.
The polite and assertive queues
The reader maintains at least one queue of pending announcements, and the politeness level decides which queue a change enters. Polite announcements wait for the current utterance to finish; assertive announcements preempt it. When an assertive announcement fires, the reader cancels the in-flight utterance, speaks the assertive content, and then must decide what happened to the cancelled utterance and to any polite content that was waiting.
The interaction is subtle. A polite announcement that was mid-way through being spoken when an assertive one arrived is simply lost, because the reader cancelled it to speak the assertive content. The assertive content itself can be interrupted by a later assertive announcement, so a burst of assertive changes results in only the last one being heard. This is the intended behaviour, and it is why assertive is reserved for genuinely urgent, rare events: it is destructive of everything else in flight, and using it for routine updates means the user hears only the most recent routine update and loses the rest.
A related mechanism, the role of alert or status, expresses the same scheduling intent through role rather than attribute. A role="alert" region behaves assertively and a role="status" region behaves politely, which lets the author mark the urgency with a semantic role instead of a politeness value. The two encodings converge on the same queueing behaviour.
Failure modes
The most common failure is the announcement that never fires because the content changed before the region was live, the dynamic-insertion pitfall described above. The second is the announcement that fires too often: a region that re-renders its content on every state change emits a mutation per render, and the user hears a stream of near-identical announcements. The aria-atomic flag and careful replacement, rather than incremental mutation, are the remedies, but they require the author to understand the mutation stream.
The third failure is the missed urgent announcement under load. If a burst of assertive changes arrives faster than the reader can speak them, the earlier ones are dropped by preemption, exactly as intended, but if an important polite announcement was waiting behind them it too is dropped, and the user never hears it. Priority is expressed as politeness, and politeness is a two-level scheme, so there is no way to say “important but do not interrupt”; the author must choose one of the two behaviours and accept its cost.
A final failure is region text that does not make sense when read atomically. A region whose changed fragment is a bare number depends on its surrounding context to be meaningful, and the default non-atomic behaviour announces only the number, leaving the user to infer what changed. This is a markup decision, not a reader bug, and it is the reason aria-atomic exists.
Two of these failures have a shared root cause: the reader announces whatever text the mutation stream hands it, with no notion of whether the change was substantively interesting. The specification has no attribute for importance, only the two-level urgency, so filtering by meaning has to happen in the script that mutates the region. An interface that updates a counter forty times wants to speak perhaps the final value, and the way to get that behaviour is to mutate the region once, not to ask the announcement mechanism to judge which of forty mutations mattered.
Relationship to the rest of the pipeline
The live region is the one mechanism in the screen reader stack that inverts the usual flow. The announcement pipeline is triggered by focus and reads a name computed on demand; the live region is triggered by a mutation and pushes a string that was already rendered. The two paths converge at the reader’s speech queue, which is why an assertive live region can interrupt a focus announcement mid-word and why the reader’s queueing policy is the shared resource both paths compete for.
References
- WAI-ARIA 1.2 — W3C
- HTML Living Standard — the aria-live attribute — WHATWG
- MutationObserver API — WHATWG