(Don't) make Narrator read things

So you’re working on the accessibility for your app, and you want to make Narrator (or any screen reader) read something important.

A calm person using a laptop holds up their hand to silence a bulky Narrator robot that's about to speakA calm person using a laptop holds up their hand to silence a bulky Narrator robot that's about to speak

Here are some ways to force a screen reader to announce something—plus some more accessible, better ways to share information with your screen reader users.

Do you need to read something?

First, you probably don’t need to announce anything at all. In my experience, devs usually want Narrator to announce UI changes, but usually:

  1. There’s a less-intrusive way to tell screen reader users, or
  2. They already understand that something changed — so you don’t need to tell them.

Heck, you’re probably following WCAG SC 4.1.3 – Status Messages, which captures that exact idea (emphasis added):

In content implemented using markup languages, status messages can be programmatically determined through role or properties such that they can be presented to the user by assistive technologies without receiving focus.

Although succinct, this already gives you a lot of reasons to avoid manually announcing anything.

First, this requirement only applies to status messages, which are very limited (to things like success/​failure, action results, & progress) and, specifically, do not include a “change of context”, like page navigation or a change of focus. Second, it requires those messages be delivered through role or properties—nothing about manual announcements.

These distinctions are really important for usability; from the same WCAG article (emphasis added):

Live regions and alerts can be usefully applied in many situations where a change of content takes place which does not constitute a status message, as defined in this Success Criterion. However, there is a risk of making an application too “chatty” for a screen reader user.

Note The purpose of this success criterion is not to force authors to generate new status messages. Its intent is to ensure that when status messages are displayed, they are programmatically identified in a way that allows assistive technologies to present them to the user.

WCAG does both things deliberately: screen readers often include their own tools for announcing content changes, and redundant (or long) announcements frustrate screen reader users.

In most cases, you don’t need to force Narrator to read anything when something happens. Let’s walk through what you should do.

4 ways to do it

If you’ve decided to announce something (double check!), there are 4 major ways to announce stuff in Narrator. From least to most invasive:

  1. Change the context: if you navigate to a new page or move keyboard focus, Narrator will usually read something. You’re done!
  2. Change properties on the focused item: screen readers automatically announce changes to the currently-focused element. Done!
  3. Use a live region: sometimes, your UI has a status bar or notification areas that you just need to announce. Live regions are a simple way to do that (in some UI frameworks).
  4. Force an announcement: when all else fails, you can force screen readers to announce things with custom UIA notifications (or, on the web, a bit of trickery with live regions).

Method 1: Change the context

Changing context is my favorite way to announce UI changes because, often, your app is already doing it — so no code change needed.

Changes of context are certain types of “major changes” to a webpage. The definition is a little fuzzy, but some things are clear: if the action you want to announce already ① opens a window/​dialog box, ② moves keyboard focus, ③ navigates to a new page, or ④ otherwise changes the page content in a way that “changes the meaning of the Web page”, then you don’t need to do anything — screen readers will already indicate to their users that something changed.

For example, when pressing a button shows a (correctly implemented) dialog box, Narrator will automatically read the dialog box. No custom code necessary.

In many cases, your action already initiates a context change — and you can rely on the system to announce that change for you.

Method 2: Change properties

The next best way to announce information is by changing properties on the currently focused control. This, too, might happen automatically, especially with built-in controls: checkboxes announce when they’re checked, buttons announce when their name changes, slider announce their new value, etc.

For example, if you already change the text of a button from Copy to Copied when its pressed, Narrator will automatically read that change. Again, no extra code necessary.

Property changes are a powerful — and underused — tool for announcing things to your screen reader users.

Method 3: Use a live region

But sometimes you just need to announce something.

While I’m a firm believer in adding statuses & descriptions to the “controlling”, focused element (like adding status to the search box itself in Method 2), sometimes that doesn’t make sense. For example, complicated form validation errors probably don’t belong as a property on the Submit button.

Like with previous methods, you can often rely on standard tools to solve this: live regions.

On the web, screen readers will automatically announce content changes in any custom live regions—and, even easier, roles with implicit live regions—on your page.

This is often exactly what you want: you display some text & you want Narrator to announce it! Live regions do just that; you simply mark the text’s container as a live region & Narrator does the rest. Like, if your search has onscreen status text 10 results, give its container element role="status"—Narrator will read changes automatically.

The Techniques section of SC 4.1.3’s documentation describes many ways to use live regions & roles. Live regions offer many configuration options (like how aggressively they should interrupt users) and have some strange behaviors (like how role="alert" regions sometimes read out when loaded, but nothing else does); it’s worth reading MDN docs or deeper guides.

Live regions are a framework thing, and not every UI framework has them. Microsoft WinUI & UWP do, but traditional Win32 apps don’t, and SwiftUI doesn’t either. Behind the scenes, frameworks convert live region changes into forced announcements. If your framework doesn’t support live regions, you can emulate them yourself — by forcing announcements, below.

Method 4: Force an announcement

If nothing above works, you come to the last option: force an announcement.

This is especially true if you’re writing a framework or complicated app — I mentioned above that Edge forces a notification when pages finish loading. But even in simple apps, some changes:

  1. are status messages, but
  2. don’t occur in something that should be a live region.

Microsoft is calling this confirmation of action. In 2020, they updated Office apps to read changes like this; for example, pressing Ctrl+B in Word will announce bold on or bold off. That change ① is clearly a status message (it’s the “result of an action” & doesn’t move focus) & it’s communicated visually to users (the “B” button becomes enabled), but ② it doesn’t correspond to a live region. The top bar can change in many non-status ways, so reading all changes there (via a live region) would be annoying. So it’s clear that forcing an announcement makes sense in some common cases.

However, you won’t find a WCAG guideline recommending or requiring confirmation of action. While I truly believe this is a gap in the guidelines, WCAG guidance assumes any status messages can be announced via a live region.

Heck — on the web, there’s not even an API for it. Whereas many desktop UI frameworks don’t support live regions, with HTML you must use a (hidden) live region to force announcements:

It’s not too bad:

Especially for web devs, it’s important to test your notifications with real screen readers and with real users. Because there’s no official guidance for doing this, it’s easy to get it wrong.

If it makes sense to force an announcement, do it. But test thoroughly.

Conclusion

And with that, you have my 4 ways of handling WCAG SC 4.1.3 – Status Messages. If you need to notify your screen reader users of changes onscreen, consider:

  1. Change the context
  2. Change properties on the focused item
  3. Use a live region, or finally
  4. Force an announcement

As always, test your code with actual screen reader users. Accessible design is design, and design means talking to your users. Hopefully these tools give you more ways to design software that’s accessible to everyone.


Thanks to Doug Geoffray and Matt Campbell for consulting on this article. Thanks to Lynne Bai and Melinda Ivanov for beta testing.

Tested using Narrator in Edge on Windows 11.