⚡IntermediateAdvanced Accessibility

ARIA Live Regions

Announce dynamic content changes to screen readers with aria-live, role="status", and role="alert".

Overview

Screen readers only announce content when focus moves to it. When content updates dynamically a success message appears, a cart total changes, an error occurs, search results load screen reader users miss it unless you mark the region as "live." ARIA live regions solve this: when content inside a live region changes, the screen reader automatically announces the new content without the user needing to move focus. Key attributes: - aria-live="polite": announces after the user finishes what they're doing - aria-live="assertive": interrupts immediately (for urgent errors only) - role="status": equivalent to aria-live="polite" - role="alert": equivalent to aria-live="assertive" - aria-atomic="true": announces the entire region, not just the changed part

Key Points

  • •aria-live="polite" announces when the user is idle use for notifications, updates
  • •aria-live="assertive" interrupts immediately use only for critical errors
  • •role="status" and role="alert" are semantic equivalents to polite/assertive
  • •aria-atomic="true" forces the entire region to be re-announced on any change
  • •The live region must be in the DOM before content is injected add the empty element on page load
  • •aria-live="off" (default) means no announcements

Examples

Status messages and alerts

<!-- Success notification (polite waits for user to finish current action) -->
<div role="status" aria-live="polite" aria-atomic="true" id="status-msg">
  <!-- JS injects messages here -->
</div>

<!-- Error alert (assertive interrupts immediately) -->
<div role="alert" aria-live="assertive" id="error-msg">
  <!-- JS injects error here -->
</div>

<script>
// Show success message
document.getElementById('status-msg').textContent = 'File saved successfully.';

// Show error (interrupts screen reader)
document.getElementById('error-msg').textContent = 'Connection lost. Please try again.';
</script>

The live region containers must exist in the DOM before text is injected.

Search results count announcement

<form role="search">
  <label for="search">Search</label>
  <input type="search" id="search" name="q">
  <button type="submit">Search</button>
</form>

<!-- Live region for result count -->
<p role="status" id="results-count"></p>

<div id="results-list">
  <!-- Results rendered here -->
</div>

<script>
async function performSearch(query) {
  const results = await fetch('/search?q=' + query).then(r => r.json());
  document.getElementById('results-count').textContent =
    results.length + ' results found for "' + query + '"';
  // Render results...
}
</script>

Loading state announcements

<div aria-live="polite" aria-atomic="true" id="loading-status">
</div>

<script>
function startLoading() {
  document.getElementById('loading-status').textContent = 'Loading results...';
}
function finishedLoading(count) {
  document.getElementById('loading-status').textContent =
    count + ' items loaded.';
}
</script>

Common Mistakes

  • Injecting the live region element dynamically it must exist in DOM before content is added
  • Using assertive for all messages it interrupts the user and becomes very annoying
  • Setting aria-live on the wrong element the attribute must be on the container that changes

Note

Keep a single "announcements" div with aria-live="polite" always present in your page layout. Your JS pushes messages there and clears them. This one element handles most accessibility announcement needs.

Exercises

Write HTML to solve each exercise. Use Preview to see it render in a live iframe, then mark as done.

Loading editor…

Completed: 0 / 5 exercises

Knowledge Check

Question 1 of 4

What is the difference between aria-live="polite" and aria-live="assertive"?