Skip to content
Putting technology to work.
Insights to guide decisions and action.

Search articles

When “Saved” Isn't Announced: Delivering Dynamic Updates to Screen Readers

Table of contents · 7 items

When a user clicks submit on an inquiry form, a toast notification reading “Submission complete” appears in the bottom right corner for three seconds. For sighted users operating a mouse, this works seamlessly. But into the ears of someone using a screen reader, nothing arrives. They felt the button press, but unsure whether it succeeded or failed, they press it again out of anxiety. The result: duplicate submissions for the same inquiry.

This is no rare edge case. As updating only parts of a screen via single-page applications (SPAs) and AJAX became standard practice, situations where “state changes without page navigation” multiplied dramatically. However, screen readers were originally designed to announce content on the assumption that entire pages reload. When part of the DOM quietly updates, that change is simply ignored unless developers explicitly provide a channel to convey it to users.

In this article, we examine proper design of the standard solution—aria-live regions—alongside the use cases and pitfalls of ariaNotify(), a new JavaScript API proposed and tested since 2025, organized around how to design, implement, and verify them in client web development. Clients commissioning projects can also use this as criteria to assess whether dynamic notifications on their sites reach everyone.

Why dynamic updates only reach sighted users

Screen readers vocalize content at the current focus location or where the user is actively reading. However, updates like toasts, validation messages, and refreshed search results typically occur away from the focus point and asynchronously from user actions. While anything appearing on screen catches the eye visually, audio output fundamentally cannot pick up changes occurring outside the current reading position.

WAI-ARIA live regions bridge this gap. By marking a specific element as an area with dynamically changing content, the screen reader automatically announces text changes when that content updates. This marking is applied via the aria-live attribute, which primarily takes two values.

<!-- 控えめに伝える: 読み上げ中の内容が終わってから通知する -->
<div aria-live="polite" id="status"></div>

<!-- 即座に割り込んで伝える: 重要な警告など限定的に使う -->
<div aria-live="assertive" id="alert"></div>

polite is non-interruptive, waiting until the current speech completes before announcing; it is used in most cases, such as save confirmations or count updates. assertive is interruptive, cutting off current announcements immediately, which MDN advises using sparingly due to its disruptive potential. It should be reserved for critical errors or warnings requiring immediate attention. role="status" can be used as a shorthand roughly equivalent to aria-live="polite", while role="alert" corresponds to aria-live="assertive".

What is crucial here is that live regions trigger based on content changing. In other words, the element must already exist prior to the update. Placing an empty <div aria-live="polite"> on the page beforehand and injecting text into it afterward—failing to follow this sequence means nothing will be read aloud.

Setting up aria-live properly

The most common implementation mistake is generating the live region element itself and applying appendChild every time a notification occurs. In this scenario, the browser merely sees a new element appear rather than existing region content changing, causing some screen readers to remain silent. The correct approach is to keep an empty region in place from the start and swap only its text nodes.

<!-- 初期ロード時から空で配置しておく -->
<div id="form-status" aria-live="polite" aria-atomic="true" class="visually-hidden"></div>
// 送信完了時にテキストだけを書き換える
function announce(message) {
  const region = document.getElementById('form-status');
  // 同じ文言を連続で入れると変化と見なされないため、いったん空にする手当てを入れることもある
  region.textContent = '';
  requestAnimationFrame(() => {
    region.textContent = message;
  });
}

announce('お問い合わせを送信しました。担当者より2営業日以内にご連絡します。');

class="visually-hidden" is a standard utility class used to hide content visually while keeping it accessible to screen readers. Because hiding via display:none or visibility:hidden removes elements from the accessibility tree and prevents announcements, techniques that push elements off-screen (such as position:absolute; width:1px; height:1px; overflow:hidden; clip-path) are used instead. Maintaining this dedicated audio notification container alongside visual toasts is a reliable pattern. When attempting to make a single DOM element handle both visual feedback and speech, announcements often become unstable due to toast animations or timing constraints, so separating visual and auditory paths prevents unexpected breakages.

Additionally, broadcasting identical wording consecutively may cause it to be treated as unchanged and skipped. In such cases, clearing the text before reinserting it or appending an invisible counter can help. Inserting requestAnimationFrame in the code example above prevents immediate same-frame writes from collapsing into a single update, introducing a brief pause to ensure the change is recognized.

The attribute aria-atomic="true" specifies that the entire region should be re-announced even if only part of it changes. However, there is a catch: attributes like aria-atomic and aria-relevant (which specify which types of changes to announce) behave inconsistently depending on the screen reader and environment combination. For example, some environments ignore aria-relevant="additions" and always read the entire region, while TalkBack with Chrome on Android reportedly ignores aria-atomic or aria-relevant and reads the whole region on every event. Because designs relying on granular controls are prone to environment-specific bugs, keeping regions simple with short, single messages is the safest path. When dynamic UIs must retain state while notifying users, handling input retention and session state must also be addressed; these implementation details are covered extensively in Session Timeouts and Accessibility.

The new ariaNotify(): benefits and pitfalls

The fundamental weaknesses of live regions are that they only fire in response to DOM modifications and their spoken content is tied directly to DOM text. Notifying users without altering content or separating visible text from spoken announcements previously required convoluted workarounds.

To solve this, the Microsoft Edge team and WICG (Web Incubator Community Group) have led proposals for ariaNotify(), an imperative JavaScript API. It allows developers to instruct announcements directly from code without establishing live regions.

// 要素または document に対して、読み上げてほしい文言を直接渡す
// priority は 'normal'(既定)と 'high' があり、
// normal は aria-live="polite"、high は assertive にほぼ相当する
document.body.ariaNotify('検索結果を24件に更新しました。', {
  priority: 'normal',
});

// 割り込み制御。先行・保留中の通知を止めて即座に伝えたい場合
saveButton.ariaNotify('保存しました。', {
  priority: 'high',
  interrupt: 'all',
});

Its advantage lies in vocalizing messages independently of on-screen display at arbitrary timings, decoupled from DOM state. Alongside priority, developers can specify interrupt (whether to interrupt preceding or pending notifications), allowing finer control over interruption levels that were difficult to express with live regions.

However, caution is warranted. The CSS-Tricks piece referenced earlier, “The Siren Song of ariaNotify(),” warns against jumping on it simply because it seems convenient. Keep two critical caveats in mind.

First, both standardization and implementations are still evolving. ariaNotify() is currently at the proposal stage for WAI-ARIA and has been tested primarily as an experimental API within Microsoft Edge origin trials (through late 2025). As of June 2026, it cannot be considered a stable standard across all browsers and screen readers, and detailed specifications may change. GitHub provides a polyfill for unsupported environments that falls back internally to live regions, making it practical to use alongside native implementations during this transitional phase.

Second, announcement is not guaranteed. ariaNotify() is an asynchronous API; calling it provides no guarantee that a screen reader will vocalize it immediately, nor does it offer any way to detect whether a screen reader is active. While this limitation also applies to live regions, being able to invoke it imperatively does not make delivery foolproof. That is why designing announcements as an audio channel parallel to visual feedback—rather than a substitute—remains an essential principle.

The differing characteristics of aria-live and ariaNotify() are summarized below.

Dimensionaria-live regionsariaNotify()
Trigger sourceText changes within the DOMDirect invocation from code (no DOM modification required)
Announced textDependent on region contentConfigurable independently of on-screen display
Standardization and supportWidely implemented and stableProposal and experimental stage (as of June 2026)
Interruption controlTwo levels: polite and assertiveGranular control via priority and interrupt

In conclusion, for production projects in June 2026, aria-live should still serve as the primary approach. ariaNotify() is best treated as a candidate for phased experimentation, provided a polyfill guarantees fallback to live regions.

Our case study: fixing silent slot updates in a booking system

At Company B, a service business operating multiple regional facilities, this issue surfaced when redesigning their web booking system as an SPA. When users adjusted dates or party sizes, the available slot list at the bottom updated via AJAX. While visually responsive, multiple visually impaired users reported through customer support that “changing search criteria feels like nothing happens” or that they had to “call by phone because it was unclear whether the booking went through.”

Upon investigation, the available slot list was being completely recreated as a new component (deleting the old list and inserting a new one), causing the live region to be reconstructed each time. As mentioned earlier, this is not recognized as a content change and results in silence across many environments. Furthermore, the submission toast was implemented purely with CSS animations, offering no auditory path whatsoever.

We implemented three targeted measures. First, we established a permanent empty aria-live="polite" region on initial render separate from the list, updating it solely with concise summaries such as “Updated to 5 available slots.” Rather than reading out the full list, this informed users of the outcome first. Second, we distinctly separated booking confirmations, submissions, and errors using role="status" and role="alert", reserving assertive exclusively for errors to interrupt ongoing speech. Third, we verified the implementation across physical screen readers (NVDA, VoiceOver, and Android TalkBack) to ensure announcements were not dropped due to environmental variances.

As a result, inquiries asking whether updates had occurred dropped significantly, visibly reducing the facility's burden of handling duplicate booking cancellations. While we evaluated ariaNotify(), considering browser support at the time, we chose not to deploy it to production and instead reinforced the implementation with dependable live regions. Prioritizing reliable delivery to all users took precedence over adopting cutting-edge APIs. This approach of improving systems by identifying who is being left behind aligns directly with our perspective in UX Research for Cognitive Inclusion.

Always verifying delivery on actual devices

The design work discussed so far only holds value once verified. Both live regions and ariaNotify() may look correct in code, yet real-world speech output often diverges across environment combinations. Automated tests and Lighthouse scores alone cannot capture whether an announcement actually fired.

At a minimum, test across these physical combinations: NVDA with browsers on Windows, VoiceOver on macOS and iOS, and TalkBack on Android. Focusing on three key checks keeps verification efficient. First, does the intended message actually read out upon updating? Second, do polite notifications avoid unnecessary interruptions to user interactions (excessive assertive disrupts reading and degrades UX)? Third, are duplicate or triplicate announcements avoided (a common issue caused by recreating regions or misconfigured attributes)?

A practical tip for testing more frequently is to prepare a single shared live region component for notifications early in development. Instead of bolting on audio pathways whenever toasts or validation rules are written, routing through a shared function like announce() from the start structurally eliminates omissions. Auditing and retrofitting audio pathways across every screen later grows exponentially harder as features multiply. Centralizing into a shared function also means that when ariaNotify() stabilizes in the future, it can be rolled out across all screens simply by updating the internal function logic, isolating the migration cost from polyfills to native APIs to a single point. This consolidation pays off when preparing for new APIs. Reviewing whether notifications hold up under font enlargement alongside Accessible UIs Resilient to Font Scaling will further enhance the robustness of your dynamic UI as a whole.

What to verify first

While the new ariaNotify() is appealing, adopting bleeding-edge APIs is not the immediate priority for existing websites. Check just once on an actual device whether three core elements read aloud properly with a screen reader: “Submitted” messages following form submissions, “Result count updated” notices during searches or filtering, and error messages. In many cases, you will discover that the audio is completely silent.

If addressing live region architecture seems likely to involve substantial rework, seeking outside expertise provides a faster path forward. Dynamic notification design is not just an implementation trick; it is an information architecture challenge about what to communicate, when, and with what priority. Managing the entire process—from research and implementation to physical device verification—is a core strength of custom client development. When considering accessibility audits for SPAs or AJAX updates, or establishing screen reader testing workflows, feel free to contact GleamHub.

Sources

Share this articleXFacebook
Kakeru Suzuki

Fascinated by the possibilities of technology, has had a deep interest in programming and digital art since student days

Turn this article's theme into your company's next step

Starting from what you want to achieve with your website.

We organize user goals, required features, and ongoing maintenance structures to determine the first steps in development and improvement.

  • Website objectives
  • Features and usability
  • Post-launch operations
Consult on web development and improvements

You can consult with us from the initial conceptual stage. Details from this article will be carried over to the inquiry form.

Receive the latest articles via email · Read the web production guide
Free download

Complete Guide to Web Production: Costs, Vendor Selection & Traffic Acquisition [2026 Edition]

We have compiled cost benchmarks, vendor selection criteria, and traffic acquisition strategies into a PDF.

The PDF and newsletter emails are currently in Japanese.

You will also be subscribed to our newsletter. You can unsubscribe at any time.