TIL … about CSS content alt texts
The relationship status between CSS content and screenreaders is complicated. This episode of "Today I Learned" figures out how to deal with Schrödinger's (ir)relevant content.

Herbert Braun
Associate Director Frontend
My current main job is not to develop but to consult a customer making their websites (more) accessible. Me and a highly experienced QA colleague put together a list of possible problems. One of his last entries irritated me:
Unintelligible CSS content is read by screenreader.
The site's developers had created a list where they replaced the list item marker with CSS content
:
ul {
list-style: none;
}
li::before {
content: "●";
}
I felt unjustly accused – accused because I have done things like that myself and unjustly so because I didn't see what was wrong about that.
What business does a screenreader have reading CSS content? To me, CSS content is equivalent to CSS background-image – strictly presentational. If it were relevant it would be in the markup.
I suspected the screenreaders ignoring the specification but after some research I found that they did so with the blessings of the WCAG, i.e. the W3C. The wise council wants screenreaders to …
Check for CSS generated textual content associated with the current node and include it in the accumulated text. The CSS :before and :after pseudo elements can provide textual content for elements that have a content model.
I still think that's a bad idea. And actually WCAG contradicts itself when they define inserting "non-decorative content" with CSS an accessibility failure. That's Schrödinger's content: It's at the same time relevant and strictly decorative.
Anyway, the screenreader makers did as they were told and we have to deal with that.
My colleague had to remind me that you can include an alt text in CSS content. It works like this:
content: "Hello, browser" / "Thatʼs for you, screenreader";
Nice – but I vaguely remembered that there was some issue with browser support and indeed: Safari started supporting this feature only a few months ago with version 17.4. So, a considerable share of browsers will ignore a content
property with alternative text. You can work around that with a fallback, of course:
content: "Hello, browser";
content: "Hello, browser" / "Thatʼs for screenreaders but not for old Safaris";
But if we work with lists there's a simpler way. The ::marker
pseudo element is a superior alternative to ::before
as it just replaces the marker without you having to fiddle with padding
and margin
and list-style-type
. ::marker
also uses content
to replace the predefined marker styles – but it seems screenreaders ignore a ::marker
's content'.
li::marker {
content: "🧑🏼💻";
}
It might be safer though to use the alt text syntax with fallback here, too – because the current CSS spec draft demands that any "generated content should be (…) available to assistive technologies." Sigh.
All that said I still have one unanswered question: Why didn't the customer's developers go through that hassle with ::before
when the result looks exactly like list-style-type: disc
… which happens to be the <ul>
default? Maybe web developers have a innate desire to rebuild the stuff that's already there – often with mixed results.