Skip to main content
Photography of Alvaro Montoro being a doofus
Alvaro Montoro

Cross-Stitcher

Drawing of a smiling woman's profile with the text Creating Accessible CSS Drawings

Creating Accessible CSS Art

css html a11y webdev

CSS art has been a thing since the creation of CSS itself. It is a great way of practicing and learning and it makes for an interesting coding challenge. But it has a big problem: CSS art is inherently not accessible.

In this post, we will not see how to create CSS drawings and illustrations (there are plenty of those posts). Instead, we will focus on some tips and best practices to make that CSS art more accessible for everyone.

After applying these techniques, your CSS art will be friendlier for screen-reader users, people with specific color needs and preferences, people with vestibular disorders or vertigo... and all without impacting your art or limiting your creativity. It's a win-win.

For a working example of the tips in the article, check this CSS illustration. And without further ado, let's see the recommendations:

  1. Identify the CSS art as an image
  2. Add alternative text
  3. Go pixel-perfect... or go for scalable design
  4. Use semantic HTML elements
  5. Be mindful with the animations
  6. Consider people's color choices
  7. Set the aspect ratio

Identify the CSS art as an image

One of the main things to do is identifying the CSS art as an image. We can do that by changing the container and adding img ARIA role:

<div role="img">
  <!-- HTML of the image -->
</div>

By adding this ARIA role, the assistive technologies will announce the container of the CSS art as an image when they reach it.

Author's note: from now on, our CSS art will be identified as an image, so the terms "CSS art", "drawing", "illustration", or "image" may be used interchangeably.

Add alternative text

Now that the screen readers announce the CSS art as an image, it is important to also provide some description or alternative text as a regular image would do with the alt attribute.

We can achieve this by adding an aria-label attribute to the image, and providing the alternative text there:

<div aria-label="The alternative text goes here">
  <!-- HTML of the image -->
</div>

This will work just fine but, in the unlikely (but possible) case of the CSS not loading properly, may not be enough for what we want. That's why we prefer using the aria-labelledby attribute instead:

<div aria-labelledby="alt-image">
  <div id="alt-image">The alternative text goes here</div>
  <!-- HTML of the image -->
</div>

This solution will include the label as text, which will be hidden using an accessible technique. There are many possibilities for doing this; a simple one can be found on The A11Y Project's website:

#alt-image {
  clip: rect(0 0 0 0);
  clip-path: inset(50%);
  height: 1px;
  overflow: hidden;
  position: absolute;
  white-space: nowrap;
  width: 1px;
}

This way, if the CSS doesn't load, the alternative text will be displayed. Similarly to how the alternative text of a native <img /> would be displayed on the page if the image fails to load.

Go pixel-perfect... or go for scalable design

Something to consider even before we start any coding: do we want a pixel-perfect drawing? or do we want something responsive that will scale nicely (at the cost of sometimes not being displayed perfectly fine)? Of course, the answers will have consequences on how we create the CSS art.

If we want to achieve a pixel-perfect result, we should use absolute units like px, cm, pt, etc. This way, our image will have a fixed size and won't be responsive (although it will scale better than raster images as it is a vectorial drawing), and we should be able to use any CSS property and value.

On the other hand, we will use relative units like %, vmin, em, etc. if we want to achieve scalability.

Going for scalability will require some additional considerations:

  • The result will be scalable and responsive... or at least, have the capacity of being responsive.
  • We should be cautious with some CSS properties that don't work with all relative units (e.g. box-shadow or border)
  • We should avoid some CSS values that don't take relative units (e.g. at the moment clip-path can be relative with polygon() but not with path())

There is nothing wrong with either approach. For artistic drawings, we may most likely opt for scalability and responsiveness; while for more practical things like icons or backgrounds, a pixel-perfect result could be more convenient.

Use semantic HTML elements

HTML5 provides many semantic elements. There is no need to use <div> for every part of our drawing, and especially for the container. The real question would be "Which one?"

Author's note: I understand my pick of semantic elements could be debatable. I will try to keep them as neutral/objective as possible.

And there's a couple of elements that seem ideal for the case: <article> and <figure>/<figcaption>. While the latter may seem the obvious choice, the former has additional semantic advantages, as we'll soon see.

On one hand, we have <figure>. A self-contained content with an optional caption (<figcaption>) that could be used as alternative text:

<figure aria-labelledby="alt-image">
  <figcaption id="alt-image">
    Here goes the alternative text
  </figcaption>
  <!-- HTML of the image -->
</figure>

This choice seems like an exact fit for what we want to do, but we also need to consider <article>: a complete, self-contained, and independent composition intended to be distributed or reused. Precisely what our CSS art is!

<article>
  <!-- HTML of the image -->
</article>

Which semantic HTML element to pick? That would be up to the author and how the CSS art will be presented to the users.

Some people already mentioned that having an <article> within an <article> isn't allowed, but that's incorrect. An article can contain another article. When there are nested articles, the inner article must be related to the outer article.

Using an <article> also allows for a more natural use of other semantic HTML elements like headings for the illustration title/alternative text, <address> for the author's information, or <time> for the creation date:

<article aria-labelledby="image-alt" 
         aria-describedby="image-info">
  <!-- The whole header would be accessibly hidden -->
  <header>
    <h2 id="image-alt">Title/Alternative text</h2>
    <p id="image-info">
      <address>
        Created by 
        <a href="https://twitter.com/alvaro_montoro">
          Alvaro Montoro
        </a>
      </address>
      on
      <time datetime="2021-06-10">June 10th, 2021</time>
    </p>
  </header>
  <!-- HTML of the image -->
</article>

Be mindful with the animations

Sometimes, CSS art includes some type of animation: a dog wagging its tail, a person moving or blinking, an airplane flying and looping...

When we include animations into our CSS art, we need to consider that there are people who may not like animations or who suffer from disorders (e.g. vestibular disorders or vertigo) that make those animations annoying and painful.

We need to provide a way to disable or replace those animations with something different. Luckily, CSS offers the prefers-reduced-motion media query that allows developers to do exactly that.

For example, we can disable an animation by doing something like this (adding the class "animated" to the elements that have animations):

@media (prefers-reduced-motion) {
  .animated {
    animation: none;
  }
}

We also need to take into account that not all animations are equal, and not all will cause problems. Instead of disabling all the animations, maybe we should consider replacing them with friendlier ones or adjusting the times.

Consider people's color choices

Some operating system allow users to enable additional accessibility features and, in some cases, CSS is able to identify them using media queries and features.

Many of them are experimental and not widely supported, but they can be used to expand our CSS art once support is more extended.

These media queries are:

  • prefers-contrast: to indicate if the user wants a higher or lower contrast.
  • prefers-color-scheme: to detect if the user requested dark/light color theme (often used for night/day modes.)
  • forced colors: to specify if the user chose a limited color palette.
@media (prefers-contrast: more) {
  /* more: higher contrast colors, borders, no transparencies */
  /* less: lower contrast colors */
}

@media (prefers-colors-scheme: dark) {
  /* dark: drawing with more contrast vs dark background */
  /* light: drawing with more contrast vs light background */
}

@media (forced-colors: active) {
  /* override properties like box-shadow, add borders, etc. */
}

The use of CSS custom properties (aka CSS variables) will make these media queries really useful and simple (we should only redefine the variable values.)

In more extreme cases, we may want to avoid some CSS properties. For example, box-shadow, which is forced to 'none' in forced-colors mode.

Set the aspect ratio

A newer property useful when creating responsive CSS art is aspect-ratio. With it, we will be able to set the preferred aspect ratio for the image, which will be used for calculating the auto sizes when scaling:

/* This CSS art will be squared */
#myCSSart {
  aspect-ratio: 1 / 1;
}

/* This CSS art will be twice as tall as it is wide */
#myCSSart {
  aspect-ratio: 1 / 2;
}

This is more of a convenience for the developer, but it can be helpful for accessibility as it ensures that the CSS art will not be stretched and distorted. And it has decent support by browsers.


This article is an extension of a presentation I made during a SydCSS meetup. You can find the video on Youtube.

Article originally published on