Deep dive: understanding live regions, part 2
Posted:
In part 1, we explored how screen reader users can be left unaware of dynamic content changes and how live regions help keep users informed when content updates without a page reload.
In this post, we'll focus on the technical side of creating and configuring live regions.
Creating live regions
There are three ways to create live regions.
Using aria-live
Adding the aria-live attribute to an element explicitly designates it as a live region. It does not change the element’s semantics, which is useful if you need to maintain the element's existing semantics. The chosen value (politeness setting) determines the update's priority. There are three aria-live values:
assertive: indicates updates to the region are high priority and should be announced immediately, interrupting any current output. Because of this, assertive should be used only for critical, time-sensitive announcements.polite: indicates that updates to the region should be presented at the next opportunity, after the current output has been spoken or when the user stops typing.off: effectively disables the live region, so changes to the text will not be announced.
See the Pen aria-live examples by James Jacobs (@jamesjacobs) on CodePen.
Using a live region role
There are five live region roles with implicit semantics that indicate content may update dynamically.
role="alert": Used to convey critical alerts. Has an implicitaria-livevalue ofassertiveand therefore should only be used for critical information, as it will interrupt the current screen reader output. Some screen readers, like NVDA, will start the announcement with "alert".role="status": Used to convey status messages - advisory information, not critical enough to justify interrupting the user / current output. Has an implicitaria-livevalue ofpolite.role="log": Used to convey a live region where new information is added in a meaningful order, and older information might disappear, like chat or messaging logs. Has an implicitaria-livevalue ofpolite.
See the Pen ARIA live region roles by James Jacobs (@jamesjacobs) on CodePen.
While the following are technically live regions, they behave slightly differently. Although they indicate that content may change, they have an implicit aria-live="off" value, so updates are not announced automatically.
role="marquee": defines an area that presents non-essential information that changes frequently, like an advertising banner.role="timer": defines a numerical counter that counts up or down.
Labelling live region roles
Live region roles also support accessible names, whereas if you use aria-live to designate a live region, the semantics of the element used will dictate if it supports the addition of an accessible name.
Generic container elements like <div> and <span> do not support accessible names, so adding an aria-label or using aria-labelledby would be invalid:
<!-- This is invalid -->
<div aria-live="polite" aria-label="Basket"></div>
However, as live region roles provide support for accessible names, you can do things like:
<!-- This is valid, though screen reader support differs -->
<div role="status" aria-label="Basket"></div>
In the above example, if the text "1 item added" is inserted into the live region, screen readers should announce the accessible name with the inserted text, for example, "Basket 1 item added". Keep in mind that not all screen reader and browser combinations include the accessible name in the announcement.
Using the output element
There’s a third option, the <output> element, which has the implicit role of status, though its use case is a little different - it’s meant to convey the results of a calculation or user action. At the time of writing, screen reader support isn’t as mature as aria-live or live region roles, so personally, I’d leave this option for now.
Configuration options
The following attributes can be used to configure and fine-tune a live region's behaviour.
| Attribute | Purpose | Allowed values | Default values | Implicit values on live region roles |
|---|---|---|---|---|
aria-atomic |
Controls whether the whole live region is announced, or only the changed part | true, false |
false |
role="alert": true
role="status": true
|
aria-relevant |
Controls which types of DOM changes should be treated as relevant updates for announcement | Space-separated list of: additions, removals, text, all |
additions text |
None |
aria-busy |
Indicates that updates are still being processed and announcements should wait until complete | true, false |
false |
None |
When using these options, test across a range of browser and screen reader combinations to make sure they work as expected. In my experience, aria-relevant="removals" produces inconsistent results and is best avoided.
A more reliable approach is to announce the change through a standard live region instead. For example, if you need to notify users that someone has left a chat, you could add a message such as "James has left the chat" to an existing <div role="status"></div>.
Use the correct politeness
Using the right politeness setting is critical to avoid interrupting screen reader users unnecessarily:
- For important messages: use assertive live regions, those that use
aria-live="assertive"or have an implicitaria-livevalue ofassertive, likerole="alert". - For status related messages: use polite live regions, those that use
aria-live="polite"or a role with an implicitaria-livevalue ofpolite.
| Use case | Announcement | Politeness setting |
|---|---|---|
| Form validation appearing as focus leaves a form field | "Error, enter your name" | assertive |
| The user is about to be signed out | "You are about to be signed out, press spacebar to stay signed in for another 10 minutes" | assertive |
| Applying a search results filter | "Results filtered by type" | polite |
| Confirming an action, like adding an item to a shopping basket | "1 Item added to basket, total items 3" | polite |
| Web chat messages | "Advisor said: Hello, how can I help?" | polite |
| Microinteractions with background processes | "Saving changes", "Changes saved" | polite |
Add to the DOM before use
For a live region to work, it must be in the DOM before it is populated with text. Inserting a populated live region into the DOM will not trigger an announcement, with the exception of role="alert". A good idea is to have the empty live region present on page load, or, in heavily JavaScript-driven applications, in the DOM as early as possible, before it’s populated with text.
Example: live region present on page load, then JS adds text
<!-- Present on page load -->
<div role="status" id="notification"></div>
<!-- Text added by JavaScript -->
<script>
// After user action (like a button press), or other event
const liveRegion = document.getElementById('notification');
liveRegion.textContent = 'Hello there';
</script>
Example: live region added by JavaScript, with text added after a short delay
// Create the live region
const status = document.createElement('div');
status.setAttribute('role', 'status');
// Add it to the DOM
document.body.appendChild(status);
// Add text after a slight delay
setTimeout(() => {
status.textContent = 'Hello there';
}, 150);
A note on role="alert"
role="alert" technically can be inserted, prepopulated with text into the DOM, and fire the announcement, though to keep things consistent and avoid any potential issues, I’d treat this the same as other live regions, and insert it into the DOM before populating it with text (with a slight delay between the DOM insertion and the insertion of text).
Visibility
Depending on the use case, you may want a live region to be visibly displayed (if the notification is relevant to all users) or visually hidden (if it’s specifically aimed at screen reader users).
If the live region is visually hidden, it must be hidden in a screen reader-friendly way, e.g., using the CSS clip method, which typically looks like this:
.visually-hidden {
clip: rect(0 0 0 0);
clip-path: inset(50%);
height: 1px;
overflow: hidden;
position: absolute;
white-space: nowrap;
width: 1px;
}
Hiding the live region with display: none, visibility: hidden, or aria-hidden="true" will remove the live region from the accessibility tree and result in updates to it not being announced by screen readers.
WCAG considerations
4.1.3 Status Messages sets requirements and definitions around status messages. The goal of 4.1.3 is to make users aware of important changes in content by letting assistive technology notify users about status changes or messages that don’t take focus.
It defines status messages as:
change in content that is not a change of context, and that provides information to the user on the success or results of an action, on the waiting state of an application, on the progress of a process, or on the existence of errors
The understanding status messages page states:
This success criterion specifically addresses scenarios where new content is added to the page without changing the user's context. Changes of context, by their nature, interrupt the user by taking focus. They are already surfaced by assistive technologies, and so have already met the goal to alert the user to new content. As such, messages that involve changes of context do not need to be considered and are not within the scope of this success criterion.
So when a status message doesn’t have focus set to it or causes other changes of context, like a page reload, we need to use live regions with the correct roles and properties to communicate that message to screen readers. Not using live regions to programmatically communicate status messages and using assertive live region for content that is not important or time-sensitive are both examples of 4.1.3 failures.
Next time
In part 3 of this deep dive we'll explore some common mistakes and questions that crop up when using live region.
