Lesson 20 · Part 4 · Live and window

Live regions, windows, and ariaNotify

A live region is a spot on the page that a screen reader reads out loud when its text changes, even though the user is not looking at it. Window roles make a dialog behave like a dialog. Neither works if you create the container at the moment of the change, or if you forget to move focus into a modal.

In one minute

In plain words

Think of a train station announcer. A polite announcement waits until the current one finishes. An assertive announcement cuts in ("Platform change, now"). A live region is the loudspeaker: it has to be installed before the train arrives. If you build the loudspeaker at the moment of the announcement, nobody hears it.

1. Put the region on the page first

Assistive technology watches a live region for changes. A region that arrives already holding text is often not spoken. So create it empty on load, then write into it. The exception is alert, which is announced when it appears and when it changes, because platforms treat alert as a notification.

Step 1. Pick a version, then press "Show message".


          
Press "Show message".

The same idea applies to React and other frameworks: do not conditionally render the live region element. Render it always, and conditionally render its text. This is WCAG 4.1.3 Status Messages: users must be told about results without focus moving.

2. Polite or assertive?

role="status" is polite: the screen reader finishes what it is saying, then speaks. role="alert" is assertive: it cuts in. Use assertive only for something the user must know now, like a failed payment. Everything else (saved, copied, 3 results) is polite.

Step 2. Imagine the screen reader is mid-sentence. Send each kind of update and read the transcript.

status (polite)

alert (assertive)

Press a button.
Try both.

3. Say the whole sentence: aria-atomic

Often only a number changes inside a sentence: "Showing 4 of 12 results". By default only the changed node is spoken, so the user hears "4". aria-atomic="true" makes the screen reader read the whole region.

Step 3. Toggle aria-atomic, then press "Next filter" to change the count.

Showing 12 of 12 results

Related properties: aria-busy="true" while you rebuild a region so half a render is not read, then false when it is done (a region left busy goes silent). aria-relevant picks which changes count (additions, removals, text, all). Most UI should keep the default.

4. Dialogs: move focus in, give it back

A dialog is a window inside the page. A keyboard user cannot see where a new box appeared unless you move focus into it. The checklist: name it with aria-labelledby, set aria-modal="true" when the rest of the page is blocked, put focus on a control inside (for a destructive question, the safe one), close on Escape, and return focus to the button that opened it.

Step 4. Choose a version. Open the dialog with the keyboard, then press Escape.

Focus is on: nothing yet

Open the dialog.

The HTML <dialog> element opened with showModal() does the focus move, the top layer, the Escape key, and makes the rest of the page inert for you. Use it when you can. alertdialog is for short, urgent questions like this one, not long forms. This supports WCAG 2.4.3 Focus Order and 2.1.2 No Keyboard Trap (a modal traps focus on purpose, and Escape is the way out).

5. ariaNotify: speak without changing the page

element.ariaNotify(message, options) is a newer browser API. It sends a message to assistive technology without needing visible text to change. The options can include a priority. If the element is not in the accessibility tree, the notification is dropped. Support is not universal, so the button below calls it when it exists and otherwise writes the same sentence into the polite status region. Never make it the only channel for an error.

Step 5. Press the button and see which path your browser takes.

Press the button.

6. Reference: live region roles

alertImportant, usually time-sensitive, text. Implicitly assertive. Use it for an error that just happened, not for a permanent hint. Do not put a focusable control inside an alert and expect the alert to be the name of that control.
statusAdvisory information that is not urgent. Implicitly polite. A save confirmation, a result count, a “copied” message. HTML: an element with role="status". This is the default you want for most UI chatter.
logA region where new information is added in order and old information stays, such as a chat log or a console. Assistive tech may only speak the new lines. Do not rebuild the whole log node on each message or you will reannounce history.
timerA numerical counter of elapsed time. Update the text on an interval the user can follow. A timer that speaks every second is hostile. Prefer a visual timer and a polite update at meaningful points.
marqueeNon-essential information that changes often, such as a stock ticker. Frequently ignored, and often a distraction. Do not use it for anything the user must hear.

7. Reference: live region properties

aria-liveoff, polite, or assertive. Polite waits for the user to pause. Assertive interrupts. Off means changes are not announced. Alert implies assertive. Status implies polite. Do not put assertive on a region that updates while the user types.
aria-atomictrue or false. When true, the whole region is announced on a change, not just the node that changed. Useful for “3 of 12 results” when only the number node updates. When false, only the changed text is queued.
aria-busytrue while you are about to rebuild the region, then false when it is stable. Assistive tech can wait for the finished content instead of reading half a render. Clear it. A region left busy forever goes silent.
aria-relevantWhich mutations matter: additions, removals, text, all, as a token list. The default is additions and text. Set removals only when a disappearance is the message. Most UIs should leave the default.

8. Reference: window roles

dialogA window inside the page. Name it with aria-labelledby pointing at its heading. Set aria-modal="true" when the rest of the page is inert. Move focus in on open, back to the invoker on close, and close on Escape. HTML: <dialog> does the focus move and the top layer. Use it.
alertdialogA dialog that contains an alert. Initial focus goes to a control inside it, often the safer action, not the destructive one. Use it for “Delete this invoice?” not for a long form.

Common mistakes

You seeWhy it is a problemFix
Message element created with its textOften not announced.Keep an empty status on the page, write into it.
aria-live="assertive" on everythingInterrupts the user constantly.Default to polite. Reserve alert for urgent errors.
Toast that disappears in 3 secondsToo fast to read, and WCAG 2.2.1 Timing Adjustable.Give it time, or let the user dismiss it.
Timer that speaks every secondHostile noise.Visual timer, polite update at milestones.
Dialog opens, focus stays behind itKeyboard user is still tabbing through the page underneath.Move focus in, return it on close.
Only ariaNotify for an errorNot supported everywhere.Keep a live region fallback.
Try it yourself
  1. Open the dialog with Enter in the fixed version. Focus should land on Cancel. Press Escape and focus should return to "Delete invoice".
  2. Switch to the broken version and repeat. Note where focus ends up.
  3. With a screen reader on, send a polite update and an assertive update and listen for which one interrupts.

Remember

Edit this example in the playground