The Visual-only icon problem

June 30, 2014

Topics: Accessibility, Web Development.

This is something I’ve been seeing a lot lately, and it’s got to stop.

Screenshot of graphic-only toggles

This illustrates the use of icon fonts. Now, there’s nothing fundamentally wrong with icon fonts; but there’s frequently something wrong with how they’re used. In this particular case, these are two links, used to toggle a particular preference. You can tab to them and activate them from the keyboard, which is great.

However, other than experimentation, there’s no way to know what they are. As a sighted user, you can guess, but without sight, there’s absolutely nothing you can do. The underlying code includes no text at all, of any kind. No title attribute, no hidden text, nothing.

And this is extremely common with the use of icon fonts — they’re implemented with no alternative text.

The alt attribute only applies to media resources, such as an img tag — but that doesn’t mean that only those resources need alternative text! Every form of graphic needs to communicate what it stands for; and icon fonts may not be implemented through an image, but they’re still a graphic. You still need to consider whether a given icon is functional, communicative, or ornamental and provide alternative text on that basis.

How to provide alternative text for an icon font

There’s a good article that provides a lot of detail on making icon fonts accessible: Bulletproof Accessible Icon Fonts by Zach Leatherman, so for a thorough look at the topic, go there. But in brief:

  1. Hide the font character itself using aria-hidden='true'
  2. If the icon needs text, supplement with a hidden text alternative <span class='screen-reader-text'>Toggle Grid Mode<span> or make the text visible, and the icon supplemental.

I’m not concerned particularly with the overall question of cross-browser support for icon fonts; but I’m certainly concerned about whether the meaning of the icon is conveyed to the end user.

What about sighted users?

So, understanding what these icons mean can sometimes be difficult to pin down. You can see the icon, but what does it mean? For a sighted user, having some form of hover-activated text can be useful – such as what’s provided by a title attribute.

Now, generally, I have a lot of arguments against the title attribute – and they still apply. The title attribute has the major disadvantage that it may or may not be read by a screen reader. As such, it’s not a good substitute for having screen-reader-hidden text. But, because it might be read, it could end up as duplication if you do both. But since it’s an attribute, not an element, it can’t be hidden to screen readers using aria-hidden.

So, the ideal (in my opinion) is to implement some form of CSS-driven tooltip, making the screen-reader-hidden text visible on :hover, and giving both groups of users access to that information.

In Summary

Font icons are awesome. They’re easy, flexible, scale nicely, load quickly (as long as you’re not going crazy with them) — but they need some support to have alternative text. Please, take that extra step to provide it!

Have something to contribute?

« Read my Comment Policy

8 Comments to “The Visual-only icon problem”

  1. “So, the ideal (in my opinion) is to implement some form of CSS-driven tooltip, making the screen-reader-hidden text visible on :hover, and giving both groups of users access to that information.”

    Easiest thing in the world is to use the span with the screen reader class. As a child or descendent of some anchor, you can relatively position the anchor so as to show/hide the span my shifting it around.

    I tend to like the offscreen method, but as that does indeed fail in rtl languages and apparently hits Yet Another VoiceOver Bug, clip could probably be better toggled. Here, you can get :hover, :focus and :active, where :active could help on mobiles/touch devices.

  2. Your solution with the <button> is viable, but will only work in screen readers that support aria-label. This is fairly good coverage, but screen reader hidden text in this context would give you more complete coverage. In this case, the visual icon is obvious, so it’s almost certainly not necessary to provide further information for sighted users, but that is not true with some more obscure icons.

  3. In this situation I use the following markup to achieved the solution

    <button class=”button button-close” aria-label=”Close”>x</button>

    This is read it as “Close button”

    And only in edge case I use the “visually-hidden” class borrowed from html5-boilerplate

    * Hide only visually, but have it available for screen readers:

    .visuallyhidden {
    border: 0;
    clip: rect(0 0 0 0);
    height: 1px;
    margin: -1px;
    overflow: hidden;
    padding: 0;
    position: absolute;
    width: 1px;

    * Extends the .visuallyhidden class to allow the element to be focusable
    * when navigated to via the keyboard:

    .visuallyhidden.focusable:focus {
    clip: auto;
    height: auto;
    margin: 0;
    overflow: visible;
    position: static;
    width: auto;

  4. No, there’s no ARIA rule for making text only available for screen readers – and won’t be, because ARIA isn’t a language for effecting the visual appearance of a web site. It would be great if there was a simple CSS pattern that hid text but made it available to screen readers, but it’s not the case. There is a rule for making text hidden only for screen readers, but that’s the opposite of what we’re looking at here.

    That is a particularly verbose rule for hiding text, as it happens; if you don’t need to support right-to-left languages, you can simply use position: absolute; left: -999em;.

  5. @Søren Birkemeyer: Whoa. That “visually-hidden” rule is quite complex. There should be an easier way to hide content visually but not from screen readers. Isn’t there an ARIA setting to make screen readers ignore display:none?

  6. @Šime Vidas: not display: none as it won’t be seen by screenreaders, either. More like this:

  7. Hidden text alternative means .screen-reader-text { display: none }, right?