CSS Can Influence Screenreaders

Introduction #

Let's say we're building a shopping list app. As we build out the app, we decide to style the list, stripping out the bullets that the browser gives us by default.

<ul>
<li>Apples</li>
<li>Bananas</li>
</ul>
  • Apples
  • Bananas
<ul style="list-style: none;">
<li>Apples</li>
<li>Bananas</li>
</ul>
  • Apples
  • Bananas

Being dutiful accessibility testers, let's run our screenreaders over the two lists. Pause for a moment and ask yourself: do you expect any difference between how the two lists are announced? Why or why not?

I was able to test the two lists with NVDA for Windows and VoiceOver for macOS. I ran NVDA against the lists on Chrome, Firefox, and even Internet Explorer. I ran VoiceOver against Chrome and Safari. Here's what I found:

... Huh.

As we keep building our hypothetical shopping list app, we implement a feature to let users add new items, complete with a shiny new "Add" button. We'll even set it to be all uppercase with CSS.

<button>
Add
</button>

<button style="text-transform: uppercase;">
Add
</button>

Upon testing the page with screenreaders, our screenreader's readout confirms that it is receiving the "ADD" text in all caps. Usually, it's totally fine for a screenreader to receive a word in all caps—they're usually smart enough to realize it's just a capitalized word. If you navigate to the above button with VoiceOver, however, you'll learn that VoiceOver has confused the capitalized "ADD" button for the acronym A.D.D.—something it definitely wouldn't have done if we hadn't changed the CSS.

These cases of CSS messing with our screenreader announcements are initially shocking, perplexing, and maybe even appalling. After all, they seem to conflict with our mental model of CSS, one that's likely been instilled in us since we started learning web development: HTML is for content, and CSS is for visual appearance. It's the separation of content and presentation. Here, by changing what screenreaders announce, it feels like CSS is encroaching on content territory.

What is happening here? Do we need to worry about every CSS rule changing screenreader announcements?

Smart Browsers #

Screenreaders aren't actually looking at the CSS.

Browsers package up an alternate version of the DOM, called the accessibility tree, which it passes to the user's operating system for screenreaders and other assistive technology to consume. Every element in the tree is defined as a set of properties that describe the element's purpose and functionality. Screenreaders peruse the tree to know what to announce. Thanks to the hard work of browser engineers, browsers have gotten really smart about building the tree. They can account for web developers' tricks—whether best practices or bad habits—and curate a more usable accessibility tree.

As much as the web development community talks about the separation of content and presentation, the truth is that it's not that easy. Between using pseudo-elements and toggling display: none; on elements to show or hide them, it's clear there can be a bit of a gray area between content and its presentation. This gray area provides a key space for browsers to optimize their accessibility trees, giving all screenreader users the same experience of the content as sighted users.

CSS's Potential Influences on Screenreaders #

What kinds of CSS-based optimizations or modifications do browsers make to the accessibility tree? Below, I've listed a few kinds that I know of. I'm sure it's not exhaustive. More importantly, these impacts will depend heavily on the user's choice of operating system, browser, and assistive technology. On the WebAIM blog, John Northup cautions us,

"It’s tempting to assert that if you do x, 'the screen reader' will announce y. Sometimes it really is just that simple, but in a surprising number of situations, it just isn’t that absolute."

John Northup, WebAIM, Screen Readers and CSS: Are We Going Out of Style (and into Content)?

Be sure to test each of the following on many different browsers and with many different screenreaders.

CSS-Generated Content #

The clearest instance of CSS-as-content is pseudo-elements, which can inject content into the page without adding it to the DOM. For instance, Firefox and Safari both support the ::marker pseudo-element,Go to footnote [1] which injects a bullet point, number, or other indicator before a list item.

We can also use the ::before and ::after pseudo-elements to inject content.