aria-atomic and aria-relevant in Practice

Permalink to "aria-atomic and aria-relevant in Practice"

aria-atomic decides whether a screen reader speaks only the part of a live region that changed or the whole region. aria-relevant decides which kinds of change — added nodes, removed nodes, text changes — count as announceable at all. Both are widely set by copy-paste and widely misunderstood; this page covers what they really do, how inconsistently they are supported, and the small set of cases in data interfaces where setting them makes a difference.

It belongs to ARIA live regions for dynamic data.

Spec reference

Permalink to "Spec reference"

aria-atomic (true | false, default false; true implicitly on role="status" and role="alert"). When true, assistive technology should present the entire region as a whole when any part changes. When false, it presents only the changed nodes.

aria-relevant (space-separated tokens: additions, removals, text, all; default additions text; role="log" defaults to additions). It tells assistive technology which mutations to report.

Support in practice: aria-atomic="true" is honoured by NVDA, JAWS and VoiceOver, with occasional quirks. aria-relevant is honoured patchily — removals in particular is ignored by several screen reader and browser combinations. The ARIA specification itself notes that authors should not rely on removals.

Criterion: SC 4.1.3 Status Messages. Neither attribute is required for conformance; both shape whether a status message is understandable when it is spoken.

What gets spoken for one change Matrix showing what a screen reader speaks when one number inside a sentence-shaped live region changes, with aria-atomic false and true. What gets spoken for one changeRegion contentChangearia-atomic="false"aria-atomic="true"Showing <span>12</span> of48012 → 14"14""Showing 14 of 480"Last sync<span>10:02</span>time changes"10:05""Last sync 10:05"One text node messagewhole text replacedwhole messagewhole messageLog with appended rowsrow addednew row onlyentire log re-read
With atomic false, a changed span is spoken alone — "14" means nothing without its sentence.

When to set them — and when not to

Permalink to "When to set them — and when not to"

Set aria-atomic="true" when a region is a sentence with a changing part: “Showing 14 of 480 results”, “Balance: 1,240.00”, “Last updated 10:05”. Without it, only the changed span may be spoken, stripped of meaning. role="status" already implies it, which is one reason to prefer role="status" for such messages.

Do not set it on a log or feed that grows by appending — every new line would re-read the whole history. role="log" defaults to aria-atomic="false" for this reason.

Leave aria-relevant alone in nearly every case. The default, additions text, covers new messages and changed text. If you find yourself reaching for removals — to announce that a row was deleted, say — write an explicit message instead: “Invoice INV-1042 deleted.” Removals support is too inconsistent to depend on, and a removal announced by itself (“INV-1042”) is ambiguous anyway.

The misapplication to name is aria-relevant="all" on a region that is updated by replacing its content. Every update is then a removal plus an addition, and readers that honour both speak the old content and the new.

Annotated code example

Permalink to "Annotated code example"
<!-- Sentence with a changing number: atomic, so the sentence is read whole -->
<p role="status" id="result-count">       <!-- role=status implies aria-atomic=true -->
  Showing <span id="shown">12</span> of 480 invoices
</p>

<!-- The same with a plain live region: atomic must be explicit -->
<p aria-live="polite" aria-atomic="true" id="sync">  <!-- SC 4.1.3 -->
  Last sync <time id="sync-time">10:02</time>
</p>

<!-- Append-only activity log: atomic false, additions only (the role's defaults) -->
<ol role="log" aria-label="Import activity" id="import-log">
  <li>Row 1 imported</li>
</ol>
// Updating only the number is fine when the region is atomic
document.getElementById('shown').textContent = '14';     // reads "Showing 14 of 480 invoices"

// Deleting a row: do not rely on aria-relevant="removals"
function onDelete(row) {
  row.remove();
  status(`Invoice ${row.dataset.id} deleted.`);            // explicit, complete message
}

A simpler alternative to atomic regions is to replace the whole text of the region every time. If the entire message is one text node, atomic and non-atomic regions behave the same, because the changed part is the whole message. Many teams standardise on that and never set aria-atomic at all.

Keyboard & AT behaviour

Permalink to "Keyboard & AT behaviour"
Setup NVDA + Chrome JAWS + Chrome VoiceOver + Safari
Atomic sentence, number changes Whole sentence Whole sentence Whole sentence
Non-atomic sentence, number changes “14” “14” Sometimes the whole sentence
aria-relevant="removals", node removed Removed text read Often ignored Often ignored
role="log", row appended New row only New row only New row only
aria-relevant="all", content replaced Old then new New only New only
Should this region be atomic? Decision tree for aria-atomic based on whether the region is a sentence with a changing part, a log that grows, or a single replaced message. Should this region be atomic?How does the region's content change?A part of a sentencearia-atomic="true"or role="status", which implies itLines appendedrole="log"atomic false, additions onlyWhole text replacedEither settingbehaves the same
The only case that needs thought is the partial update — and replacing the whole text avoids it.

Integration context

Permalink to "Integration context"

These attributes are the fine-tuning layer on top of politeness. Decide polite or assertive first, as in choosing between polite and assertive aria-live regions, then pick the role, as in role status, alert and log compared — the role sets sensible defaults for both attributes.

KPI cards that update a value inside a labelled card are the classic atomic case in dashboards; see accessible KPI cards and sparklines.

Spoken words per update, 20-line log Bar chart of words spoken when one line is appended to a 20-line log with aria-atomic false versus true. Spoken words per update, 20-line logrole="log", atomic false6 wordslog with aria-atomic="true"120 words — whole history re-read
An atomic log re-reads its whole history on every new line.

Gotchas

Permalink to "Gotchas"

Nested live regions. A live region inside another live region produces double announcements in some readers, and the outer region’s aria-atomic can pull in the inner region’s text. Do not nest them.

Atomic regions with interactive content. A region containing a button re-reads the button’s name on every update. Keep live regions to text.

Frameworks that patch text nodes. React and Vue may update only a text node inside a span, which is exactly the “partial change” case. If a message sometimes reads as a bare number, the framework is doing a minimal update inside a non-atomic region.

Design system notes

Permalink to "Design system notes"

A shared status component can hide these attributes from product teams entirely. Offer two components — a StatusMessage that always renders role="status" and replaces its whole text, and an ActivityLog that renders role="log" and only appends — and do not accept aria-atomic or aria-relevant as props on either. The role defaults are correct for their intended use, and every custom combination teams have reached for in practice has been a bug.

Testing checklist

Permalink to "Testing checklist"

FAQ

Permalink to "FAQ"
What does aria-atomic="true" do?

It tells assistive technology to read the entire live region whenever any part of it changes, instead of only the changed part. It is implied by role=“status” and role=“alert”.

Should I use aria-relevant="removals" to announce deleted rows?

No. Support is inconsistent and several screen readers ignore it. Write an explicit status message such as “Invoice INV-1042 deleted” when something is removed.

Do I need aria-atomic if I replace the whole message each time?

No. When the region’s content is a single text node that you replace entirely, the changed part is the whole message, so atomic and non-atomic regions behave the same.

Why does my result count region only say the number?

Because the region is not atomic and your framework updated only the text node holding the number. Make the region atomic, or use role=“status”, which implies it.

Permalink to "Related"

← Back to ARIA Live Regions for Dynamic Data