How Can I Make My Icon System Accessible?

Here's a question I got the other day?

Would you suggest icon fonts or inline SVGs for a complex single page application? And are there specific accessibility concerns for either? Accessibility is especially important for us because schools use our products. I ask because we are currently in the process of unifying and setting up an icon system.

I don't think I would make the choice based on what "kind" of website I was building, so let's ignore that part of it.

I also think SVG icon systems are just better than icon fonts, so let's assume that.

The accessibility question is the interesting bit, so let's cover that.

There are two ways an icon might be used:

  1. Standalone: icon needs to convey meaning all by itself
  2. Decorative: icon is just visual sugar - the words around it convey the meaning.

Pattern for Standalone Icons

I'm adapting this from Accessible SVGs by Heather Migliorisi

<!-- role="img" so that the SVG is not traversed by browsers that map the SVG to the "group" role -->
<!-- aria-labelledby pointing to ID's of title and desc because some browsers incorrectly don't use them unless we do -->
<!-- if you are using a <use>-based icon system, this would be a <symbol id="unique-id"> below, but otherwise the same -->
<svg role="img" viewBox="0 0 100 100" aria-labelledby="unique-title-id unique-desc-id">

  <!-- title becomes the tooltip as well as what is read to assistive technology -->
  <!-- must be the first child! -->
  <title id="unique-title-id">Short Title (e.g. Add to Cart)</title>

  <!-- longer description if needed -->
  <desc id="unique-desc-id">A friendly looking cartoon cart icon with blinking eyes.</desc>

  <!-- all the SVG drawing stuff -->
  <path d="..." />
</svg>

Pattern for Decorative Icons

Remember the idea with a decorative icon is that if it wasn't there at all, it wouldn't matter. So, we hide it from AT:

<button>
  <svg aria-hidden="true" viewBox="0 0 100 100">
    <!-- or <use>, if using a <symbol>-based icon system -->
    <path d="..." />
  </svg> 
  Add to Cart
</button>

But... icons might be used either way.

Right. So you probably should prepare your icons such that they are ready to be used as standalone, meaningful icons. Then, if they are used in a decorative context, slap aria-hidden="true" on the <svg>.

So your icon system might manifest like...

<Icon icon="icon-cart.svg" standalone="true" />

or

<?php
  $standalone = false;
  include("icon-cart.php"); // SVG with PHP logic sprinkled in
?>

or

<%= render "icon/cart", :locals => {:standalone => true} %>

or whatever.

Let's say you have to go with icon fonts though.

Things happen.

So much icon font usage is like this:

<i data-icon="a"></i>

Or a variation, like it's using "Private Use Area" for the attribute value or generates unique class names for each icon or whatever:

.icon-camera:before {
  content: "\e90f";
}

To make a standalone icon, that's pretty easy, you just put aria-hidden="true" on the element.

If you need a meaningful standalone icon, that means more HTML. From Bulletproof Icon Fonts, this is the recommended structure:

<span class="icon-fallback-glyph">
  <span class="icon icon-hamburger" aria-hidden="true"></span>
  <span class="text">Menu</span>
</span>

Now the icon itself is hidden (because it uses a weird meaningless font), but there is real text there that can be read as expected by AT. You can hide the text visually as well. Assuming you have some feature testing in place here's the CSS that hides/shows as needed:

.icon-fallback-text .icon {
  display: none;
}
supports-fontface.supports-generatedcontent.icomoon .icon-fallback-text .icon {
  display: inline-block;
}
.supports-fontface.supports-generatedcontent.icomoon .icon-fallback-text .text {
  clip: rect(0 0 0 0);
  overflow: hidden;
  position: absolute;
  height: 1px;
  width: 1px;
}

Remember linked icons too!

If an icon (any of these ways) is a link, make sure there is either regular HTML text in there that clearly announces what it does, or, you provide an aria-label that does:

<a href="link" aria-label="See Picked Pens">
  <svg> 
    <use xlink:href="#icon-codepen"></use>
  </svg>
</a>