Unicode For A Multi-Device World

About The Author

John is a front end developer at Linney Design, and has been building web content for well over a decade. Creating rich, accessible sites is his focus, but any … More about John Holt ↬

Email Newsletter

Weekly tips on front-end & UX.
Trusted by 200,000+ folks.

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.”

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:

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.
More after jump! Continue reading below ↓

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.

Why you should bother about unicode.
Left to right: @font-face icon using a ligature in Chrome; @font-face icon using a ligature in Internet Explorer 9 (yep, nothing there); @font-face icon using a Unicode fallback in Opera Mini; @font-face icon using a PUA code point in Opera Mini (yep, nothing there). (Large version)
SVG and @fontface subpixel rendering.
Left: SVG with grayscale anti-aliasing. Right: @font-face icon with subpixel rendering. (Both in Firefox). (Large version)

(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.

Tools like IcoMoon make it simple to generate icon fonts.
Tools like IcoMoon make it simple to generate icon fonts. (Large version)

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.

Shapecatcher will return a selection of similar looking icons for you.
Shapecatcher will return a selection of similar looking icons for you. (Large version)

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.

Variation in Emoji characters.
Variation in Emoji characters. Left to right: Firefox (Windows 7), Chrome (Windows 7), Chrome (Android 4.2.1), Opera (Windows 7) and Safari (iOS). (Large version)

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.

Unicode character 260E shown in Firefox, Chrome, Internet Explorer and Opera on the same Windows 7 machine, and on iOS.
Unicode character 260E shown in Firefox, Chrome, Internet Explorer and Opera on the same Windows 7 machine, and on iOS. (Large version)

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.


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.

    .visiblyHidden {
    /* https://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;

<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.

An example of the variation in glyphs from a single Unicode code point with different page encodings.
An example of the variation in glyphs from a single Unicode code point with different page encodings. (Large version)

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.

It still doesn’t work in Firefox
Top to bottom: no unicode-range used; unicode-range added for numerals; unicode-range for numbers viewed in Firefox. (Large version)

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:

HTML (hexadecimal number)&#x????;


HTML (decimal number)&#????;


(Note: no x as in the hexadecimal number)

CSS content (number must be in hexadecimal)\????

.heart:before { content: '\2665'; }

unicode-range (number must be in hexadecimal)U+????

@font-face {
font-family: heartFont;
src: url(fonts/heart-font.ttf) format("opentype");
unicode-range: U+2665;

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:

Smashing Editorial (ds, il, al, ml)