Announcing changes

Tell somebody what just happened, without taking their focus or talking over them.

12of 16
≈ 12Min read
5Parts

The problem

You add a door to the quote and a small message appears: "Added — 3 doors in the quote". A sighted user sees it in the corner of their eye. A screen reader user is told nothing at all: focus has not moved, nothing they are reading has changed, and the page does not narrate itself.

A live region is the instruction to announce a change without moving focus. It is the right answer for toasts, form-validation summaries, search-result counts, "saved" indicators and progress.

The two you need

Assertive interrupts whatever is being read, mid-word. Use it for errors and almost nothing else; a page full of assertive regions is unusable.

and they exist before anything happens
<p role="status"></p>     <!-- polite: waits until the user pauses. aria-live="polite" -->

<p role="alert"></p>      <!-- assertive: interrupts. aria-live="assertive" -->
RolePolitenessUse for
statuspolitesaved, added, "4 results" — the normal case
alertassertiveerrors and anything the user must hear now
logpolitea chat or a console, where order matters
progressbar—a value, not an announcement

The rule people fall over

Assistive technology watches live regions for changes. A region that appears already containing text is not a change to anything — it is a new element, and it is usually missed. Render the empty container with the page, then write into it.

the region must already be in the DOM
// nothing is announced: the region and its content arrive together
document.body.insertAdjacentHTML("beforeend", '<p role="status">Added</p>');

// announced: the empty region was there, and its text changed
document.querySelector("#status").textContent = "Added — 3 doors in the quote";
And do not announce everything

A live region on a list that updates while typing narrates every keystroke. Debounce it, announce the result rather than the process ("4 doors found"), and never put one on a clock.

The attributes around it

aria-atomic="true" is usually what you want for a short sentence: without it, changing "3 doors" to "4 doors" may announce only "4". aria-relevant exists and is almost never needed.

what gets read when part of it changes
<div id="status" role="status" aria-live="polite" aria-atomic="true">
  <!-- atomic: read the whole region, not only the bit that changed -->
</div>

<div aria-busy="true">…</div>   <!-- "still loading, do not announce yet" -->

When a live region is the wrong tool

  • A dialog — that takes focus instead.
  • A route change — move focus to the new heading, as in the Focus lesson.
  • An error on a field — attach it with aria-describedby and focus the field; a live region as well is usually too much.
  • Anything the user did not cause. An announcement with no context is worse than silence.

Practice