When JavaScript changes content on the page (a message appears, a counter updates, search results load), sighted users see it. A screen reader user hears nothing, unless you tell assistive technology to announce it. Live regions do that.
The basic idea
An element marked as a live region is watched. When its text changes, the screen reader reads the new text.
<button id="add">Add to cart</button>
<p id="cart-status" aria-live="polite"></p>
<script>
let count = 0;
document.querySelector("#add").addEventListener("click", () => {
count++;
document.querySelector("#cart-status").textContent = `${count} item${count === 1 ? "" : "s"} in your cart`;
});
</script>
The paragraph is silent until its content changes, then a screen reader says, for example, “2 items in your cart”.
aria-live values
| Value | Behaviour |
|---|---|
polite | waits until the user is idle, then announces. Use this almost always. |
assertive | interrupts immediately. Only for urgent messages. |
off | not announced (the default) |
Handy roles with built-in live behaviour
| Role | Meaning | Equivalent to |
|---|---|---|
role="status" | a status message (saved, loading, results count) | aria-live="polite" |
role="alert" | an important error or warning | aria-live="assertive" |
<p role="status" id="saved"></p>
<div role="alert" id="error"></div>
Rules that make it work
- The live region must already exist in the page when it loads, ideally empty. A region added together with its message is often not announced.
- Change the text inside it. Hiding and showing it with CSS is less reliable.
- Keep messages short and clear.
const status = document.querySelector("#status");
function announce(message) {
status.textContent = ""; // clear first, so repeats are announced again
setTimeout(() => { status.textContent = message; }, 50);
}
Good uses
- “Item added to cart”, “Saved”, “3 results found”.
- Form errors and success messages (Accessible Validation).
- Loading states: “Loading…” then “Loaded” (Loading States).
- Live scores, chat messages, progress.
Mistakes
- Announcing too much. A region that changes every second becomes noise. Announce results, not progress ticks.
- Using
assertivefor normal updates. It is rude, and it interrupts what the user is reading. - **Moving focus and announcing** the same thing, which can double the speech.
- Putting a big container in a live region: every small change inside is read again.
- Making visual-only changes (colour, icons) with no text.
Alternatives
Moving focus to new content (a heading, an error summary) is often better than a live region when the user needs to act on it. Use a live region for information the user does not need to act on right away.
Visually hidden messages
If the message is only for screen reader users, keep it in the page but hide it visually:
.visually-hidden {
position: absolute; width: 1px; height: 1px; overflow: hidden; clip-path: inset(50%); white-space: nowrap;
}