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:
- Standalone: icon needs to convey meaning all by itself
- 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>
One note about the style block you use to visually hide the text: there are a few minor caveats with old IE, some AT/browser pairings, and text wrapping that this collection of styles address:
I kept your selector, though I usually have this as a utility class (
.visually-hidden
) as that describes its sole purpose.Somewhat related, I am seeing more and more use of emoji in web sites in place of icons. This is a problem for screen reader users and Léonie Watson outlines how to minimize that on her site: https://tink.uk/accessible-emoji/
For olds (like me) who can see but who do not understand what a particular emoji is meant to convey, I tweaked it to support keyboard / tap users as well: http://adrianroselli.com/2016/12/accessible-emoji-tweaked.html
Why not use the good old
alt
tag and avoid aria altogether?Because
alt
is for<img>
and we’re not using<img>
in either case here.Proposed for WCAG 2.1 :
Add technique for identifying CSS generated content-images
Add icon fonts to the definition of non-text content
Another point worth considering when using generated content like in the icon font example shown previously is that visual media sometimes needs or benefits from providing alternative text for speech output.
Separation of concerns asides, there is now means to provide alt text for generated content directly in your CSS. It’s in working draft status and you can read more at https://www.w3.org/TR/css-content-3/#alt
So you can augment the audible output of an icon font directly in the CSS file without having to edit the HTML.
So for example as the alt property is supported on iOS when using VoiceOver and we can either mute or provide more readable alternatives when using icon fonts.