A while ago, I was working on a website that required a number of icons. “No problem,” I thought. “I know how to handle this. I’ll use an @font-face
icon set for high-resolution screens. It’ll be a single file, to reduce HTTP requests, and I’ll include just the icons that I need, to reduce file size.”
“I’ll even use a Unicode character as the base of the icon, so that if @font-face
isn’t supported, then the user will still see something like the intended icon.” I felt pretty pleased with myself.
That is, until I ran the page in the device lab.
On some devices, a number of the icons weren’t showing. Yet on the same devices, others were, so clearly it wasn’t an @font-face
issue. It must have been the underlying Unicode.
Further Reading on SmashingMag:
- All About Unicode, UTF8 & Character Sets
- Open Device Labs: Why Should We Care?
- You, Me And The Emoji: Character Sets, Encoding And Emoji
- Towards A Retina Web
What To Do?
- I could have used ligatures for the
@font-face
icon. These are great for accessibility but aren’t supported in Internet Explorer 9 or earlier. - I could have used the default “blank” Unicode characters (the PUA ranges), which apps such as IcoMoon default to. But, again, Internet Explorer has issues, and if the user has set a default font (if they are dyslexic, for example), then they wouldn’t see anything meaningful.
- There are some good arguments for using SVG, but I like the flexibility of
@font-face
: Changing the color on hover is very easy, for example, and@font-face
icons will render with subpixel anti-aliasing, whereas SVG uses grayscale.
I was happy with the underlying Unicode characters because they gave a nice fallback for users on Windows Phone 7, Opera Mini and old BlackBerrys that don’t support @font-face
. Instead, I wanted to determine which Unicode characters I could safely use.
So, I started doing some testing. And that led to some more testing, and then some more.
So far, I’ve looked at 107 Unicode characters on 78 devices and 5 screen readers. I’ll be honest — it’s not the most fun I’ve ever had. So, I’ll save you the trouble and share some of the problems and potential solutions that I’ve come across.
Why Bother?
But before we get to that, let’s just make sure that this is all worthwhile.
(Oh, and if I hear murmurings of “Opera Mini? No one uses that,” then consider that there are 70 million more Opera Mini users than iPads in the world.)
To my mind, using the underlying Unicode character seems the most robust approach, offering a visual icon to the greatest number of devices and browsers.
How Do You Do It, Then?
If you’ve not used Unicode characters with @font-face
icons, don’t worry. Tools like IcoMoon make it simple. When you’ve selected the icons that you want to use and clicked to generate the font, you’ll see a page that shows all of your chosen icons, with their name and the Unicode code point that they will be mapped to. This will usually be something like e600
, and this number is what you’ll need to replace with the hexadecimal value of your chosen Unicode character.
So, how do you know which Unicode character to use? You could go through one of the many resources that list all 110,000 characters, but you’ve probably got better things to do. Shapecatcher makes life a bit easier: Draw the shape you’re after, and it will return a list of similar-looking characters.
Pick the one you like best, copy the Unicode hexadecimal value, and paste it into IcoMoon. Once you’ve done this for all of your icons, download the @font-face
package as normal.
So, Which Characters Should You Use?
Well, I’ll have to defer to Jeremy Keith’s answer for everything about the Web: “It depends.” In my testing, support for any given Unicode character varies hugely (and by “support,” I mean that either the @font-face
icon appears or that the underlying Unicode character appears, rather than the user seeing an empty rectangle or a question mark or nothing at all). Even Unicode characters in the same “block” of related symbols often have quite different levels of support.
But I can provide a little help. Once you know which Unicode character you want to use, run it through Unify, which will give you a support score based on the devices that I’ve tested with so far.
If you’re happy with either the @font-face
icon or the Unicode character being displayed, you should see very high levels of support. Some icons work on everything that I’ve tested so far.
The big exception is Emoji characters, which I would stay away from. Around half of the devices that I’ve tested on don’t show the @font-face
icon, and many don’t show anything meaningful at all. Additionally, iOS replaces them with fixed-size colored graphics, so trying to scale them with font-size
won’t work.
Also, if a device can render a Unicode character, it will usually also be able to render an @font-face
icon that uses that character. So, you’ll need to decide on the tradeoff: Using an icon font gives you more control over the design, but then you’d be adding to the page’s weight and delaying the rendering of the icon until the font has loaded.
If you’re just using a Unicode character directly, then be prepared for a variety of visual styles. Even on the same computer, browsers can render the same character in quite different ways.
In many projects, the consistency and control you get from using an @font-face
icon is worthwhile. Putting only the icons that you need in the font file will minimize downloading time.
Saying anything more about @font-face
icons is pointless because Zach Leatherman has written such an excellent and well-researched article.
Accessibility
Zach looks in depth at the accessibility implications of using icons in this way, and I’d like to add a few thoughts.
There are two main problems here: The screen reader will either completely ignore the icon (which would leave the user not knowing that there is something there to interact with) or announce something seemingly irrelevant. For example, if you used Unicode character 2261
for your “burger” icon, it would either be ignored or be announced as “Equal” or “Identical to”, depending on the screen reader. Neither of these would help the user understand what this element does.
The best approach depends on what the icon is for. If it merely adds visual flourish to a text label or heading, then I’d recommend hiding the icon from screen readers. In this case, rather than adding icon fonts directly to the element in the standard way (i.e. by using the :before
or :after
CSS pseudo-element), add it to a span inside the element. This way, you can add aria-hidden=“true”
to the span, which will prevent it from being announced in a reasonable number of screen readers.
<span aria-hidden="true">≡</span> Menu
However, if the icon will be used on its own, without anything else to provide meaning or context, then I’d recommend hiding the icon as before but adding a text label that is hidden visually. This will provide a cue that there is something to interact with.
<style>
.visiblyHidden {
/* http://css-tricks.com/places-its-tempting-to-use-display-none-but-dont/ */
position: absolute;
overflow: hidden;
clip: rect(0 0 0 0);
height: 1px; width: 1px;
margin: -1px; padding: 0; border: 0;
}
</style>
<span aria-hidden="true"> ≡ </span><span class="visiblyHidden">menu</span>
Does font-family
Help?
I’ve heard the recommendation that you should include fonts like Arial Unicode MS in a font-family stack that is being used to display Unicode symbols, but that doesn’t guarantee that the symbol will appear for everyone. If a browser can’t find a font with the required Unicode character from those you’ve listed in the font-family
declaration, then it will try to find any font in the OS that will display it.
Consistency is hard to come by, though, because it will depend on the fonts available on that particular device and on the browser being used. Browser manufacturers are free to look for any matching fonts however they think best — and for the sake of performance, the browser might stop searching if it’s taking too long.
By all means, include Arial Unicode MS or Segoe UI Symbol in a font-family
declaration if you like how they render the symbols you’re using, but you’ll be relying on that font being installed on the device. If you want to control how the symbol looks, you’re best off with an @font-face
icon.
Detecting Unicode Support
So, can JavaScript come to the rescue? Well, Modernizr does have a couple of tricks up its sleeve. One approach compares the width of a commonly supported character against one that’s likely to produce a fallback character and, if different, determines that the first character has rendered as expected.
Another focuses on trying to determine Emoji support by drawing the character onto a canvas
element and then checking a single pixel to see whether it’s been filled in by the character. The problem, as we’ve seen, is that Unicode support isn’t a boolean; just because a browser displays one character doesn’t mean that it will display another. So, I wouldn’t rely on these methods generally, but they might be useful in some circumstances.
Encoding Your Unicode
Mentioning encoding here would probably be worthwhile if you are working with Unicode. Encoding tells a browser how to interpret any Unicode characters on a page. If the encoding used by your text editor differs from the settings of your end user and there are no instructions to help the browser make sense of it, then the user will see a lot of seemingly random characters or question marks.
Fortunately, pretty much everything on the Web these days uses UTF-8, so this isn’t the headache it used to be. However, it doesn’t hurt to make sure, and this is why you’ll hear of the recommendation to include <meta charset=“utf-8”>
in the <head>
of your documents. If you’ve specified the encoding in the HTML file, then you don’t need to specify it again in any linked CSS files — it will be assumed to be the same.
If you’ve done this and you’re still seeing strange characters, then check that your HTTP headers are set to UTF-8 as well. Any encoding set there will override the meta charset
declaration.
However, if you do have to work with a different encoding type, then avoid “embedded” characters (i.e. just pasting the single character shape directly into the text editor). If you use the full numerical reference, then the character will be unaffected by the document’s encoding type.
In other words, if you want to use 2665
for a heart icon, then use ♥
, not ♥. Embedding symbols in this way is the safest approach, but adding accents and so on in body copy is not recommended because it will make the code harder to read and maintain. In this instance, getting the encoding right is better.
Hand-Picking Fonts
One feature of @font-face
that’s often overlooked is unicode-range
. This allows you to specify that a particular font is being used for just a few Unicode characters.
But why would you want to?
Perhaps you have found the perfect font for your body copy, but you would prefer one or two punctuation marks from a similar font, or perhaps you want to use old-style figures but your body font doesn’t include them. Using unicode-range
lets you use that alternate font for just those characters that you want to swap out.
You may specify a single character, a comma-separated list or an entire range of characters to be rendered in the alternate font.
It has a slightly different syntax from other methods of working with Unicode. You’ll need to add U+
before the character’s hexadecimal number. So, if you wanted a different font for all of your numbers (which are represented in Unicode between 0030 and 0039), you’d use something like this:
@font-face {
font-family: 'oldStyleFigures';
src: url(fonts/old-style-figures.ttf) format("opentype");
unicode-range: U+30-39;
}
Then you would add this:
p {
font-family: oldStyleFigures, Arial, sans-serif;
}
This means that all paragraph text will use Arial, except for numbers, which will use the oldStyleFigures font.
The downside? Of course, there’s a downside. It still doesn’t work in Firefox — the alternate font will be applied to all characters, not just the ones you’ve specified in the range.
However, Drew McLellan has a workaround for the lack of support in Firefox.
Unicode Syntax Cheat Sheet
How you add a Unicode character will determine the method that you need to refer to it. Here’s a quick summary:
Where | Syntax | Example |
---|---|---|
HTML (hexadecimal number) | &#x????; |
|
HTML (decimal number) | &#????; |
(Note: no |
CSS content (number must be in hexadecimal) | \???? |
|
unicode-range (number must be in hexadecimal) |
U+???? |
|
It Doesn’t End There
That’s it for now, but plenty of useful articles and tools are out there. Here are a few that might help you out:
- “Articles, Best Practices and Tutorials,” W3C Internationalization Activity A large collection of articles covering encoding and internationalization topics
- “Bulletproof Accessible Icon Fonts,” Zach Leatherman, Filament Group In case you haven’t read Zach’s article about icon fonts
- “Creating Custom Font Stacks With Unicode-Range,” Drew McLellan, 24Ways More detail on using
unicode-range
- Icomoon A great tool to build a custom icon font
- Shapecatcher Really useful for visually finding a particular Unicode character
- Unify My resource gives you an idea of how supported a particular Unicode character is across browsers and devices.