Be Careful with Dynamic Accessible Names

Many of my clients try to reduce the number of controls on a screen by replacing them with single controls that change their name based on their purpose (what they unironically call reducing complexity).

For example, presenting a download button that also acts as its own progress indicator and completion message. Or a submit button that animates to indicate the process is happening, and then maybe cries a little when it fails.

The challenge is that many of these re-used controls change their accessible name without any consideration of what that means for users who rely on the accessible name — screen reader and voice users.

That new accessible name is not always conveyed to these users, and the results can be problematic.

Don’t Trust the Inspector

I am a big fan of using the browser developer tools and their built-in accessibility inspectors to identify potential gotchas. When you see CSS display properties remove semantics from a control, for example, the browser’s developer tools can save the day without needing to fire up a screen reader.

The problem is that the accessibility inspectors are an abstraction of the accessibility tree (which gets handed off to assistive technology via the platform accessibility APIs). Which means they may not match.

Here are two examples of where the browser shows us a change has been successfully made to the accessibility properties of a node, but the screen reader is likely getting different information.

Chrome’s accessibility inspector shows the accessible name for the control change, but we hear JAWS announce the original value instead.
Firefox’s accessibility inspector shows the accessible name for the control change, but we hear NVDA announce the original value instead.

Results of Testing

I threw together a rather simple chunk of code to test how accessible names are reported and supported. It is not thorough.

For example, I do not go through all the variations of the Accessible Name and Description Computation from HTML Accessibility API Mappings 1.0 (no placeholder, no title).

I also do not try many variations. For example, have an image with alt text which provides the accessible name to its <button>, and I have an SVG with aria-label that does the same, but I do not also pair those with other techniques on the same control. The permutations can get big, and mostly I am just here to prove a point.

Screen Readers

Voice Control

I chose accessible names that are hard to speak (because I am a dolt), and some of them are visibly hidden (so they would fail 2.5.3), but for the testing they were passable. I am not a regular voice user, so I recommend testing on your own patterns with skilled users.

If it seems I got a bit lazy there, I did. I could have modified my example code and run at this again, but I still have cookies to bake and this sentence takes less time to write than a whole new set of tests.

Example

The example I used for my testing. You can visit the pen directly at Codepen or try it in the debug view for testing.

See the Pen QWKvQLx by Adrian Roselli (@aardrian) on CodePen.

You can fork this, modify it, and run your own tests.

Wrap-up

Avoid changing the accessible name of controls on the fly. It may not get conveyed to users, or it may become oddly verbose, confusing users.

I did not test named regions. I don’t see dynamic accessible names on those enough to worry. Yet?

Update: 4 January 2021

The research for this post helped inform the pattern in my latest, Multi-Function Button. Essentially I demonstrate how to build a button that acts as a trigger and message container, and how to do it accessibly.

Eight of the different visual styles this button can have based on use.

Update: 10 January 2024

Darin Senneff has done some fresh testing in Dynamic accessible descriptions reference. His key takeaways:

5 Comments

Reply

I refrain from altering aria-label strings exactly for the above reasons. I prefer toggling an aria-pressed prop rather than changing a button’s “play” [aria-]label to a “pause” label. I do wonder if setting an aria-live prop on a control would yield any different results.

Neil Osman; . Permalink
In response to Neil Osman. Reply

I should have referenced Sarah Higley’s post, Playing with state, where she discusses the specific use case of a play/pause button. She also has a chart of dynamic accessible name support.

In response to Neil Osman. Reply

I’ve seen aria-live used exactly the way you mentioned to get around this exact problem. It does seem to “work” in the sense that the updated button text is announced, although since the role is not also announced it could be potentially confusing.

I generally stick to Adrian’s advice of avoiding the situation entirely, but I think the aria-live approach may work in some situations like the “loading” button—since the main objective is informing the user that the activity is occurring. Still not ideal, of course, but better than nothing.

In response to James Catt. Reply

You are correct that making a control into a live region has inconsistent results. I should have addressed that in my response.

A live region separate from the control can work, and something like a “loading” control could rely on that (since we know aria-busy is effectively useless and which I mention in my post on skeletons).

In response to James Catt. Reply

James, my newest post, Multi-Function Button, shows how you can have a live region along with a control. I was writing that piece when I put this post together but it was not ready yet so I could not reference it.

Leave a Comment or Response

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <s> <strike> <strong>