Traditionally, bitmap formats such as PNG have been the standard way of delivering iconography on websites. They’re quick and easy, and it also ensures they’re as pixel crisp as possible. Bitmaps have two drawbacks, however: multiple HTTP requests, affecting the page’s loading performance; and a lack of scalability, noticeable when the page is zoomed or viewed on a screen with a high pixel density, such as the iPhone 4 and 4S.
The requests problem is normally solved by using CSS sprites, combining the icon set into one (physically) large image file and showing the relevant portion via background-position. While this works well, it can get a bit fiddly to specify all the positions. In particular, scalability is still an issue. A vector-based format such as SVG sounds ideal to solve this, but browser support is still patchy.

The rise and adoption of web fonts have given us another alternative. By their very nature, they’re not only scalable, but resolution-independent too. No need to specify higher resolution graphics for high resolution screens!
That’s not all though:
- Browser support: Unlike a lot of new shiny techniques, they have been supported by Internet Explorer since version 4, and, of course, by all modern browsers. We do need several different formats, however!
- Design on the fly: The font contains the basic graphic, which can then be coloured easily with CSS – changing colours for themes or
:hoverand:focusstyles is done with one line of CSS, rather than requiring a new graphic. You can also use CSS3 properties such astext-shadowto add further effects. Using-webkit-background-clip: text;, it’s possible to use gradient and inset shadow effects, although this creates a bitmap mask which spoils the scalability. - Small file size: specially designed icon fonts, such as Drew Wilson’s Pictos font, can be as little as 12Kb for the .woff font. This is because they contain fewer characters than a fully fledged font. You can see Pictos being used in the wild on sites like Garrett Murray’s Maniacal Rage.
As with all formats though, it’s not without its disadvantages:
- Icons can only be rendered in monochrome or with a gradient fill in browsers that are capable of rendering CSS3 gradients. Specific parts of the icon can’t be a different colour.
- It’s only appropriate when there is an accompanying text to provide meaning. This can be alleviated by wrapping the text label in a tag (I like to use
<b>rather than<span>, due to the fact that it’s smaller and isn’ t being used elsewhere) and then hiding it from view withtext-indent:-999em. - Creating an icon font can be a complex and time-consuming process. While font editors can carry out hinting automatically, the best results are achieved manually.
- Unless you’re adept at creating your own fonts, you’re restricted to what is available in the font. However, fonts like Pictos will cover the most common needs, and icons are most effective when they’re using familiar conventions.
The main complaint about using fonts for icons is that it can mean adding a meaningless character to our markup. The good news is that we can overcome this by using one of two methods – CSS generated content or the data-icon attribute – in combination with the :before and :after pseudo-selectors, to keep our markup minimal and meaningful.
Our simple markup looks like this:
<a href="/basket" class="icon basket">View Basket</a>
Note the multiple class attributes. Next, we’ll import the Pictos font using the @font-face web fonts property in CSS:
@font-face {
font-family: 'Pictos';
src: url('pictos-web.eot');
src: local('☺'),
url('pictos-web.woff') format('woff'),
url('pictos-web.ttf') format('truetype'),
url('pictos-web.svg#webfontIyfZbseF') format('svg');
}
This rather complicated looking set of rules is (at the time of writing) the most bulletproof way of ensuring as many browsers as possible load the font we want. We’ll now use the content property applied to the :before pseudo-class selector to generate our icon. Once again, we’ll use those multiple class attribute values to set common icon styles, then specific styles for .basket. This helps us avoid repeating styles:
.icon { font-family: 'Pictos'; font-size: 22px: }.basket:before { content: "$"; }
What does the :before pseudo-class do? It generates the dollar character in a browser, even when it’s not present in the markup. Using the generated content approach means our markup stays simple, but we’ll need a new line of CSS, defining what letter to apply to each class attribute for every icon we add.
data-icon is a new alternative approach that uses the HTML5 data- attribute in combination with CSS attribute selectors. This new attribute lets us add our own metadata to elements, as long as its prefixed by data- and doesn’t contain any uppercase letters. In this case, we want to use it to provide the letter value for the icon. Look closely at this markup and you’ll see the data-icon attribute.
<a href="/basket" class="icon" data-icon="$">View Basket</a>

We could add others, in fact as many as we like.
<a href="/" class="icon" data-icon="k">Favourites</a>
<a href="/" class="icon" data-icon="t">History</a>
<a href="/" class="icon" data-icon="@">Location</a>

Then, we need just one CSS attribute selector to style all our icons in one go:
.icon:before {
content: attr(data-icon);
/* Insert your fancy colours here */
}
By placing our custom attribute data-icon in the selector in this way, we can enable CSS to read the value of that attribute and display it before the element (in this case, the anchor tag). It saves writing a lot of CSS rules. I can imagine that some may not like the extra attribute, but it does keep it out of the actual content – generated or not.

![]()
This could be used for all manner of tasks, including a media player and large simple illustrations. See the demo for live examples. Go ahead and zoom the page, and the icons will be crisp, with the exception of the examples that use -webkit-background-clip: text as mentioned earlier.
Finally, it’s worth pointing out that with both generated content and the data-icon method, the letter will be announced to people using screen readers. For example, with the shopping basket icon above, the reader will say “dollar sign view basket”. As accessibility issues go, it’s not exactly the worst, but could be confusing. You would need to decide whether this method is appropriate for the audience. Despite the disadvantages, icon fonts have huge potential.


Comments
Comments are ordered by helpfulness, as indicated by you. Help us pick out the gems and discourage asshattery by voting on notable comments.
Got something to add? You can leave a comment below.
13/12/2011
Because of the usability-issue (braille-reader, screen-reader) you can use the abbr-tag to describe the use of your font-char, therefore most screen-readers will read-out the title-param of abbr and the user gets the meaning of the ‘icon-character’.
I’m not 100% sure, but as it seams NVDA, JAWS and VoiceOver for iOS this works — on Mac OS X (unfortunately) not…
Example: <abbr title=“Attachment Icon”>A</abbr>
Vote Helpful or Unhelpful
12/12/2011
Very cool trick. I’m only concerned about the a11y issues…
IIRC, it’s not all screenreaders. Apple’s voiceover is one of the few that read out loud generated content via css. But I’m not aware if the latest versions have added support for this in the rest of the gang: jaws, windows eyes and nvda.
Vote Helpful or Unhelpful
12/12/2011
A couple of little issues in this recap of the font-icon technique.
1. :before and :after are pseudo-elements, not pseudo-classes
2. The @font-face technique that is referenced is not the most bulletproof method at the moment. This one is – Further hardening of the bulletproof syntax
3. The last testing results I read showed that most screenreaders (but not VoiceOver) don’t read CSS generated content (but probably should), and there are support issues for @font-face on mobile – Dingbat Webfonts: Great potential, but we see (and hear) accessibility issues
Vote Helpful or Unhelpful
12/12/2011
I think the solution to the accessibility issues would be really simple: use unreadable characters. There are characters which screen readers won’t read (such as â—¯, â—‰, â—Ž, and ⦿) so we could use them when making the icon fonts.
Vote Helpful or Unhelpful
12/12/2011
Thanks for sharing this technique Jon. The screen reader issue might be a concern but you inspired me to try it on an upcoming project. Nice work.
Vote Helpful or Unhelpful
12/12/2011
When using multitouch zoom in Lion the webkit-background-clip icons do get blurry. However, if you use ctrl/cmnd +/- to zoom in they look nice and sharp.
Odd, I hadn’t realized that they represented two different kinds of zoom before.
Vote Helpful or Unhelpful
12/12/2011
Very nice technique, hadn’t seen the data- method before! But the screenreader problem is fairly serious.
Also the demo page isn’t working on my Android Gingerbread browser, although webfonts are supported ( http://www.google.com/webfonts works at least) so I just see the original characters ($, k, t etc.)
Both make me wonder: couldn’t they have put their new characters in some far-out character area? Aren’t there areas of unicode reserved for weird symbols?
I know that you’d then have to escape the unicode to use it in “content”, but presumably it would fail back to a blank rather than a recognised but confusing character?
Vote Helpful or Unhelpful
12/12/2011
I have tried to make the process of making icon fonts a little easier. I’ve made a font generator, which presents you with a large number of icons (currently offering 310 icons). You can easily search and select the ones you want and then assign them to letters to generate your font.
You can check it out here: http://keyamoon.com/icomoon/
A demo page with a few samples: http://goo.gl/dHUpa
Using data attributes, as mentioned in this article, is smart. Doesn’t IE8 have any problem with content:attr() though? Up to now, I’ve been using class names, for each icon.
Vote Helpful or Unhelpful
12/12/2011
Probably the best solution for the screen reader problem is assigning the icons to the <a href=“http://www.fileformat.info/info/unicode/block/supplementary_private_use_area_a/index.htm”>supplementary private use area</a> blocks of unicode.
This is what Florent suggested in my <a href=“http://forrst.com/posts/Which_characters_are_not_read_by_screen_readers-rsy”>forrst post</a> regarding which characters are never read by screen readers.
I have not tried this and I’m not sure if there are any browser issues; but if it works, I will upgrade my <a href=“http://keyamoon.com/icomoon/”>icon font generator</a> to use these supplementary characters.
Vote Helpful or Unhelpful
12/12/2011
Maybe a good idea would be to add aria-hidden=true to the psuedo-element? Do screen readers properly ignore the element then?
Vote Helpful or Unhelpful
12/12/2011
If the screenreader supports the aria-hidden selector it is a fairly good solution to the accessibility issue, but if a user uses his own stylesheet this will just show the character used.
I think this is a typical solution to improve the life of developers without keeping in mind the end-users. I do love this solution, but in the end the old-fashioned solutions might be more appropriate until this is fixed in both browsers and screenreaders.
Vote Helpful or Unhelpful
12/12/2011
@Emil How would you add that attribute to a CSS pseudo element?
This might also work:
.icon:before { speak: none;
}
By the way, I can’t figure out how to put links in my comments here on 24ways! Admins please fix my previous comment. And please enable some basic HTML tags? :/
Vote Helpful or Unhelpful
12/12/2011
This is a brilliant tip! Thanks Jon. It’s the kind of thing you could safely apply if making web apps targeted for an environment where you know exactly what browser they’re going to be run in.
Not sure I’d be happy using it in a production website where millions of people are going to be seeing it, many using IE — fortunately (?) I don’t get to work on those very much!
Vote Helpful or Unhelpful
12/12/2011
@font-face has been widely supported for long, but :before and :after pseudo elements are only supported from IE8+ :)
Vote Helpful or Unhelpful
12/12/2011
Great tip :-) the only thing I’m not sure about is your use of <b> rather than <span>, it doesn’t seem semantic.
Vote Helpful or Unhelpful
12/12/2011
Thanks for sharing this tip.
Just don’t use random characters when there are appropriate Unicode characters like U+2665 ♥ available:
<a href=”/” class=“icon” data-icon=“k”>Favourites</a>
shold be
<a href=”/” class=“icon” data-icon=“♥”>Favourites</a>.
Using appropriate characters ensures that the desired symbols will be displayed (in some system font that contains a glyph for that particular symbol) even if the desired webfont is not loaded.
Check the Unicode charts for symbols.
Vote Helpful or Unhelpful
12/12/2011
I love this idea, and used it recently (very simply) for the cheatsheet I put together for Perch – http://quick.grabaperch.com/
I went back and forth as to whether to include the technique in the new version of CSS Anthology, ultimately deciding not to because of the accessibility concerns you mention. I don’t like to recommend something to a beginner audience that might have a bunch of caveats as to whether it should be used on a particular project. I’m not an accessibility expert though and I’d love to see this technique discussed by those who are.
Vote Helpful or Unhelpful
12/12/2011
I love the idea of this but find the bad text aliasing in Firefox spoils it every time.
Until there is a way of doing: -webkit-font-smoothing: antialiased; in gecko, I will stick with sprites.
Vote Helpful or Unhelpful
12/12/2011
Jon, thanks for this excellent post – I’ve been playing with this approach for while and will definitely come back to your article.
Thank you :)
Vote Helpful or Unhelpful
12/12/2011
To solve the semantic issues, try mapping your glyphs to the Unicode private use areas. That’s pretty much what they’re intended to be used for.
Also, you’ll want to use a robust font-embedding detection script before using in a live environment – loads of the webkit based mobile browsers report false-positives for @font-face support, so you’ll end up with loads of rogue (non icon) characters dotted around your page on those devices if you’re not careful.
Vote Helpful or Unhelpful
12/12/2011
Probably also worth noting that although IE has supported font-embedding on the desktop since IE4, it’s not supported on the version of IE9 that comes with Windows Phone (up to 7.5 anyway)
Vote Helpful or Unhelpful
12/12/2011
Great article Jon. This technique has been brilliant for us as it’s drastically simplified the preparation and delivery of image assets on a lot of our mobile focused apps, where that really matters.
There has been suggestions to map the various glyphs to corresponding unicode characters (in Pictos case), which I think would greatly aid the accessibility issues. I would certainly adopt this approach in a bespoke typeface.
Vote Helpful or Unhelpful
12/12/2011
But why overload ASCII characters with unrelated meanings, just to make them appear as icons (even if you are trying to hide them, screen readers etc are starting to show generated text too).
This is what the Unicode private use area is for – use it.
Also, maybe time to re-examine your opinion that SVG support is ‘patchy’.
Claimer – involved in the development of PNG, CSS. SVG and WOFF.
Vote Helpful or Unhelpful
12/12/2011
Love this technique but I do wish all these articles had some kind of key for what browsers are supported. I know that may feel like a rather negative thing to dwell on whether the whole 24 ways concept is about a positive, festival calendar of ideas, but transparency is always a nice thing.
The unfortunate fact is that :before/:after pseudo elements aren’t supported by IE7 at all, which is still very much an important browser for many clients as we (the development community) have only just convinced our clients that IE6 isn’t an option!
Vote Helpful or Unhelpful
12/12/2011
I agree with the thrust of your article, Jon, in so far as icons in web fonts have huge potential. However, assuming we eventually have great screen rendering and sufficiently refined control in CSS, they ought to be mapped to unicode code points that already exist for a vast majority of the common symbols we need in interfaces.
My view is that other techniques should be a last resort, and dingbats should be avoided for all the right reasons. For more on why, see my article on icons, unicode, and dingbats and some of the comments, in particular the succinct comment of Joe Clarke .
For projects that warrant it, a custom typeface could easily be created, using the correct unicode symbol mapping, and private use code points for custom icons. Wouldn’t that be something!
Thanks for the interesting read. :)
Vote Helpful or Unhelpful
12/12/2011
It’s very encouraging! I love the automatic scaling, it is simply necessary for some projects! For my next project I’m sure I’ll use this technique, thank you!
Vote Helpful or Unhelpful
12/12/2011
I love the idea and I’m all for ingenuity in development but why use this when a more apt technique exists? All modern browsers support SVG as background images (and in the image element).
Considering the accessibility concerns and difficulty in creating custom fonts, I’m struggling to see any benefits here? Perhaps rapid prototyping is one, but then icons aren’t important at that stage. The ability to apply text styles is a marginal advantage if any, one could edit SVG icons just as easily.
I have a little SVG demo here:
http://dbushell.com/demos/css-sprites/
Fallbacks for older browsers (IE7/8) is a little tricky but with a bit of progressive enhancement theory can be solved.
Dingbat fonts are a cute and esoteric idea, but I struggle to see a real necessity for them.
Vote Helpful or Unhelpful
12/12/2011
Very nice idea. The only problem is the subpixel issue on the icons (blurry edge).
Vote Helpful or Unhelpful
13/12/2011
Thanks for the feedback everyone! The main point seems to be regarding the semantics of the characters used for icons. There’s a few points with this:
- Yes some characters can be mapped to existing equivalents (such as the heart symbol for favourites mentioned above), but in the scheme of things, this is the minority. Many icons don’t have equivalents. – Yes, the private area of unicode would be better (and also prevent the letter being read out), but this will add another layer of complexity in specifying the correct characters. If we has something like copypastcharacter.com for this it would help a lot – In my mind, icons are just another form of language, but the labels associated with them are the most important. For me, its the labels where the semantics matter most (but if we can use an equivalent unicode character, so much the better).
It’s interesting that the aspect I consider the biggest obstacle – that you can only add new icons if you’re able to create a font – hasn’t been picked up. To me that’s a far bigger problem than semantics of the character used, as you’re relying on what the creator of the font has offered to serve all your needs! :)
Vote Helpful or Unhelpful
13/12/2011
@David Bushell – Simply presenting another option, rather than presenting it as THE option. The advantages are (re-iterating parts of the article):
- Someone without drawing skills can implement them – They’re scalable – They’re resolution independent – IE support isn’t an issue (although quality of the rendering is)
Thanks for the SVG sprites demo though – hadn’t seen that technique before. What do you do for IE7-8, serve a PNG?
Vote Helpful or Unhelpful
13/12/2011
All good points. There is much to consider.
With reference to how many of the icons we commonly use are mapped in unicode, I’d love to see a comparison between common icons and existing unicode symbol code points as well as the newish transport and map symbols. I have a completely unsubstantiated feeling that the coverage is not bad at all. :)
Vote Helpful or Unhelpful
13/12/2011
Yeah I concur on the <b> thing… Plus the b tag literally yells at screen readers. And is outdated.
Otherwise interesting article, good food for thought! If you are up for creating a font. :)
Vote Helpful or Unhelpful
13/12/2011
I’m surprised no one has mentioned Raphaël yet, if browser SVG support is a concern.
b.
Vote Helpful or Unhelpful
13/12/2011
@Jon Hicks — thanks for the reply, I can certainly see it as an option in some circumstances, but then SVG is also scalable and there are plenty of vector graphics available too, so I don’t think either technique could call an advantage in that respect.
IE7-8 support is tricky, but yes a PNG fallback. I think conditional comments with .oldie on the <html> element would be best but that’s ignoring Firefox 3.6 etc. Testing for SVG support with JavaScript is another way, but then the PNG fallback is downloaded before SVG is detected. You could do it the other way around and default to SVG, only applying a PNG if support is not detected.
Difficulty comes with relative sizes when the background-size property is not supported. That’s more of an issue with my sprite example and not specifically SVG, though you could argue web fonts work better in that example.
There are issues with both options but for most circumstances and certainly in the near future I’d say SVG has the upper hand.
One concern I really have with web font icons is when clients say they “don’t like” a particular icon — always happens! Suddenly you have to edit a font or find a new one. That could be a nightmare. Editing a vector graphic is a tad easier for most people.
Vote Helpful or Unhelpful
13/12/2011
We’ve been using this technique to great effect on Top10.com. We designed a custom font file in order to cut down on file size and have full control over the symbols we use.
The biggest drawback for us has been rendering and aliasing issues at small sizes. The vector icons simply don’t look as crisp as their bitmap equivalents.
The data attribute tip and the idea of using the Unicode Private Use Area area are good ideas – thanks for the tips.
Vote Helpful or Unhelpful
13/12/2011
is @font-face supported by IE9? I’ve been working on a site recently and trying to avoid using images for icons. @font-face works fine for Chrome and FF but displays characters instead of symbols in IE8 and 9.
Also, what is the fallback technique you guys are using in the case of older IE versions?
Thanks
Vote Helpful or Unhelpful
13/12/2011
Sal, @font-face is supported in IE5,6,7,8,9 :)
Just gotta use an .eot in older versions. Fontsquirrel generator is your friend.
Vote Helpful or Unhelpful
14/12/2011
@Luke / @Sheila: Since the definition of the <b> element changed in HTML5 (it has become a way to mark “stylistically offset†text), it seems like a good fit in this case.
In my experience, no modern screen reader is affected by <b>, <i>, <strong> or <em> with normal settings. Older screen readers may have announced <strong> and <em> elements, but in latest versions of JAWS you needs to be in Proofreading mode for these elements to affect the speech output. While I think this is wrong – I’d expect at least <strong> or <em> to affect the output by default – I don’t think it would ever be right for <b> elements to effect speech output, HTML5 or otherwise. The <i> element is a different matter, since it can be used to indicate a change in language in HTML5.
Vote Helpful or Unhelpful
14/12/2011
Using the @font-face method sounds good, but like someone else’s comment, aliasing on certain browsers and at smaller sizes is poor (depending on the font files).
I find that using HTML entities works better for websites that need a hand full of icons.
However if you’re creating a web app and need lot’s of custom icons then this is probably the best method.
Vote Helpful or Unhelpful
14/12/2011
Thanks for the great post.
Anyone can point me to some useful resource to help me create custom pic to font
Vote Helpful or Unhelpful
15/12/2011
The only Problem I see, its almost impossible to hide those characters from blind people, there is a Interesting stackoverflow* question about it. (@JON GIBBINS)
Great article by the way!
*( http://stackoverflow.com/questions/8478173/how-to-target-a-braille-screen-reader-via-css )
Vote Helpful or Unhelpful
15/12/2011
…Well, kinda academic approach, I think. The certain icon character is defined in CSS where it is displayed as ascii. Nice idea, though.
Vote Helpful or Unhelpful
15/12/2011
Published an article about an accessible solution (using JavaScript/jQuery and WAI-ARIA) on my blog: A better way to use icon fonts.
Vote Helpful or Unhelpful
16/12/2011
I have effectively solved the problem with screen readers. My Icon-Font Generator now offers an option for assigning your icons to the “Supplementary Private Use Area” of the Unicode. Watch this short video demo to see Apple’s VoiceOver in action. You can see that VoiceOver does not read any extra letters. With this update, IcoMoon is the first, and the only icon font out there that is fully compatible with screen readers. You can download a free version if you are interested.
Vote Helpful or Unhelpful
19/12/2011
Very interesting use of web fonts!
But whenever I print a page which uses them, my default local font is always used.
Should I take your fine article and confine your great advice to the online world only, or is there something I don’t know, that would allow for successful printing?
Yes, print is a waste of paper but, like it or not, people still like the printed thing. Some of the time!
If you are interested, IE8 prints web fonts perfectly, but Chrome and Firefox 8 do not.
Vote Helpful or Unhelpful
26/12/2011
I am really impressed by this technique I didn’t know. Combining vector images for SEO purposes (in order to appear in Google Images for example) and icon fonts seems to be the best strategy. Thank you for the tip.
Vote Helpful or Unhelpful
30/12/2011
@Peter Uppington – Perhaps using a print.css would help?
Vote Helpful or Unhelpful
01/01/2012
Really like this method a lot. It does have drawbacks, but I’m in favor of it (at least for the projects I’m working on at the moment).
I think @keyamoon deserves a shoutout, too, for his work on IcoMoons. His font generator which allows you to choose from his 300+ icons to create a customized kit and map it to Supplementary Private Use Area of Unicode is pretty sweet.
Just bought it today and is working great for our use.
Vote Helpful or Unhelpful
12/12/2011
The correct place to put the characters are Unicode Private Use Area (PUA) on positions U+E000 to U+F8FF (you have 6,400 code locations) on the Basic Multilingual Plane.
You also have PUAs on Plane 15 and Plane 16.
And, there are some icons here and there, like dignbats on positions U+2700 to U+27FF, and emoji, on positions U+1F30x to U+1F5Fx.
Joseph Wain (of Glyphish fame) evaluated creating a font, but it was deemed too difficult to maintain. (http://www.formspring.me/glyphish/q/237553463913877252)I’m considering doing something like this for my personal use.
Vote Helpful or Unhelpful
Impress us