Skip to main content

Alt text for CSS generated content

Posted in Accessibility, CSS and Development

There’s an interesting feature in Safari 17.4, released last week. It now supports ‘alt’ text for content added with CSS (via the ::before pseudo element and the content property). I’m not sure how I feel about this.

On the one hand it’s great because it gives developers more control over their output but, on the other hand, I question why content is able to be added to the page with CSS at all; isn’t that HTML’s job!?

Separation of concerns

As Dennis Pintilie Alexandru writes about HTML, CSS, and JavaScript:

each language has it’s different sections or concerns of a website … HTML … is used for the content and structure of the website … CSS … is used for the styling of the website … JavaScript … is used to define the behavior.

In fact, Tim Berners-Lee, inventor of the HTML markup language, always intended this to be the case:

The separation of document structure from the document’s layout had been a goal of HTML from its inception in 1990.

I know CSS can have a pretty big impact on accessibility; to name just a few examples:

  • the display property
  • use of list-style: none;
  • horribly low contrast between text and its background

But adding content to the page with CSS is a step to far for me.

Blurring the boundaries

You see, anything added to the page with content is accessible, and this is the issue addressed in this WebKit release:

perhaps we want to prefix certain links with the little ⓘ icon to let users know this item leads to more detailed information. That symbol might be read by screenreader as “Circled Latin Small Letter I” or “Information source combining enclosing circle”, neither of which do a good job communicating the intended purpose. Perhaps a better experience would be to simply hear “Info:”

The CSS example they give is:

.info::before {
content: "ⓘ" / "Info:";
}

This would target any HTML with the info class, like this:

My favourite small wild cat is the <a class="info" href="https://en.wikipedia.org/wiki/Pallas%27s_cat">manul</a>.

Which would leave the document looking something equivalent to:

My favourite small wild cat is the <a class="info" href="https://en.wikipedia.org/wiki/Pallas%27s_cat">Info manul</a>.

And the browser builds an element that has the accessible properties of:

Name
Info manul
Role
Link

Speech recognition software users

So we’ve got a disconnect between the visual and the accessible name, which is a similar issue to using icon-only buttons.

In our ‘favourite small cat’ example the visible label’s text reads “manul”, meaning speech recognition software users would, sensibly, say the command “Click Link label”. Unfortunately, what they need to say is “Click Info Link label” since the word ‘Info’ has been added to the underlying accessible name via the CSS.

This leaves the user with a few options:

  • Try to guess what the accessible name is
  • Use a ‘mouse grid’
  • Show numbers next to all interactive items on the page

These are all work-arounds that give our users a less than straightforward experience.

What if CSS doesn’t load?

It’s an edge case, but possible, that the CSS fails to load; leaving the user with a style-free page. The good news is that this would remove the label/name mismatch issue for speech recognition software but the bad news here would be if content added via the CSS content property was integral to the user’s understanding.

No semantics

The output of anything entered in the content is limited to text. The example from WebKit is relatively safe from a semantics point of view as there’s no markup in there, but it’s worth mentioning that even if you add HTML in there, it’ll be output as text. So you might try to do the right thing by hiding the accessible output like this:

.info::before {
content: 'ⓘ' / '<span class="aria-hidden"> Info:</span>';
}

But that will be rendered plan text rather than HTML, so the accessible name will be <span class="aria-hidden"> Info:</span> rather than nothing, as we might have hoped by using that code.

For the record, this is the right behaviour in my opinion, but it doesn’t change that adding content to the page via CSS in the first place is problematic.

Time travel

Of course all of that is just a whinge. Adding the alt text to content is the right thing to do since:

The real fix for this would be to go back in time, Terminator style, and tear up the proposal for content to allow anything other than decoration. Of course, that would come with its own downsides, but that’s another post in an alternative timeline.

So what do we do?

If you’re designing and coding responsibly, you should only be using content to add decorative elements to the page, meaning you’ll probably never need the content property’s ‘alt’ text functionality.

Accessibility in your inbox

I send an accessibility-centric newsletter on the last day of every month, containing:

  • A roundup of the articles I’ve posted
  • A hot pick from my archives
  • Some interesting posts from around the web

I don’t collect any data on when, where or if people open the emails I send them. Your email will only be used to send you newsletters and will never be passed on. You can unsubscribe at any time.

More posts

Here are a couple more posts for you to enjoy. If that’s not enough, have a look at the full list.

  1. Avatars and alt text

    I really enjoyed Nicolas Steenhout’s recent article on Alt text for avatars or user photos. But there is a context where I would break his rule…

  2. Upgrading from iPhone 13 mini to 16 Pro

    I get a new phone every 3-ish years, give mine to my wife, and now she gives hers to our daughter. I got a 16 Pro this year! Here’s the skinny.