Published at
Updated at
Reading time
This post is part of my Today I learned series in which I share all my web development learnings.

Developers often use before and after pseudo-elements (generated content) to style elements. With a few lines of extra CSS, it is possible to include icons, images, or even add text without adjusting the HTML.

Unfortunately, using content in pseudo-elements can cause accessibility issues. Just because your generated content isn't defined in HTML, it doesn't mean that it is not picked up by assistive technology like screen readers.

Let's look at an example:

.new-item::before {
  content: "β˜…";

The above CSS prepends the β˜… symbol (black star) to the inner content of elements with the class new-item. And this might be all great from a visual perspective, but it has unexpected side effects for screen readers.

<a class="new-item" href="#">go to new things</a>

This link is now presented with a star before the inner text go to new things. But screen readers will read out "Black star go to new things", too. This experience is not great!

Today I learned that the content property supports a way to define alternative text for generated content.

.new-item::before {
  /* "black star" and element content is read out */
  content: "β˜…";
  /* "Highlighted item" and element content is read out */
  content: "β˜…" / "Highlighted item";
  /* Generated content is ignored and only element content is read out */
  content: "β˜…" / "";

That's pretty cool because you can provide text alternatives for generated content right in CSS! How's the browser support?

MDN Compat Data (source)
Browser support info for Alternative text after /

Webkit shipped support recently and we're waiting for Firefox.

So the rule stays the same: use generated content carefully. Going with a separate element in combination with aria-hidden="true" is probably are more accessible approach for now.

Edits part 1 – define the alternative text in HTML or custom properties

Andrea Giammarchi pointed out that having alternative text in CSS is not a scalable or maintainable solution, and I agree.

The spec defines that it is possible to specify the alternative using an element attribute. Unfortunately, the spec does not define if custom properties should be allowed – I don't see why they shouldn't, though.

.new-item::before {
  /* "black star" and element content is read out */
  content: "β˜…";
  /* Attribute content and element content is read out */
  content: "β˜…" / attr(data-star-alt);
  /* Custom property and element content is read out */
  content: "β˜…" / var(--star-alt);

Unfortunately, using attr didn't seem to work in Chrome with Voiceover.

Edits part 2 – improve browser support using feature detection

Zoltan Hawryluk pointed out that you could feature detect the browser support of content: '' / '' with @supports and then use the Safari-only alt property. Unfortunately, the only thing I could find about alt is this article by Bruce Lawson).

@supports (content: "x" / "y") {
  .new-item::before {
    content: "β˜…" / "Highlighted Text:";

@supports not (content: "x" / "y") {
  .new-item::before {
    content: "β˜…";
    alt: "Highlighted Text:";

Considering feature detection and the alt property, it boils down to only Firefox not supporting alternative text for generated content. Bummer!

Additional resources

My friend Manuel published an excellent follow-up post that you might want to check out, too – Here’s what I didn’t know about β€œcontent”.

And also, Adrian Roseli went deeper into the browser support topic of Alternative Text for Generated Content.

Was this TIL post helpful?
Yes? Cool! You might want to check out Web Weekly for more quick learnings. The last edition went out 8 days ago.
Stefan standing in the park in front of a green background

About Stefan Judis

Frontend nerd with over ten years of experience, freelance dev, "Today I Learned" blogger, conference speaker, and Open Source maintainer.

Related Topics

Related Articles