Alternative Text for CSS Generated Content

Relying on images that come from CSS has always been risky from an accessibility perspective. CSS background images, in particular, must either be purely decorative or be described to the user in some way.

The risk is no different for images coming from CSS generated content using content: url(foo.gif) (typically paired with ::before and ::after). Addressing it is often a bit trickier.

A long time ago using CSS generated content was a WCAG auto-fail. Today using content: to insert plain text can be a boon for users — from browsers using it to present quote marks for rendering <q> to authors relying on it for hints.

Too often, however, I see developers relying on images in the generated content to convey important information to users. The following construct is not uncommon:

label.required::before {
 content: url(star.png);

For this to convey to a user that the field is required, the image has to load and the user has to be able to see it. If either is not the case, this can be a problem.

The Future

CSS Generated Content Module Level 3 (Editor’s Draft) allows an author to specify alternative text for images (referenced through url()) or other non-text content (glyphs). Section 1.2. Alternative Text for Accessibility offers two examples, both of which involve following the non-text content with / "text".

Using plain text with an image:

.new::before {
 content: url(./img/star.png) / "New!";
  /* or a localized attribute from the DOM: attr("data-alt") */

Using a blank value with a strictly decorative glyph:

.expandable::before {
 content: "\25BA" / "";
 /* a.k.a. ► */
 /* aria-expanded="false" already in DOM,
   so this pseudo-element is decorative */

Unfortunately support is not where we need it today to rely on it. Further, for a browser that does not support the / "text" syntax, using it will invalidate the entire declaration, preventing your image or glyph from appearing. This could be some de facto progressive enhancement (if considered carefully) or you could just repeat your code without the text alternative.


I wanted to compare the current state of CSS generated content alternative text with the trusty <img alt=> construct. Granted, authors both forget and write terrible alt text all the time, so we can expect the same with CSS. But it is a good idea to get a baseline understanding.

I am using it as plain text, as a heading, and as the accessible name for each of a link, a field, and a button. I also tried it with an emoji as the text alternative to maybe head that chaos off early.

If the embed does not work, visit the pen directly or try it in debug mode for your own testing.

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



If you want to find a solution that works for each major platform, then do not use the CSS approach yet. Stick with images.

If you don’t care about iOS users, then Chrome is the only cross-platform browser that will work today, though that includes Chromium-based browsers (Edge, Opera, Brave, etc).

In supporting browsers, if your CSS generated content image does not load, its alternative text will not display. With the <img> element there is a good chance the alt will display.

CSS generated content will not localize easily. In the future, attr("data-alt") can potentially get around that unless you rely on automated translation tools. If you need your content to auto-translate, then the CSS approach is not for you.

No matter which of the two techniques you end up targeting for your users, avoid emoji as your alternative text.

One Comment


Very nicely analyzed as usual, thank you!

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>