Any web designer or front-end developer worth their salt will be familiar with the CSS @font-face rule used for embedding fonts in a web page. We’ve all used it — either directly in our code ourselves, or via one of the web font services like Fontdeck, Typekit or Google Fonts.
If you’re like me, however, you’ll be used to just copying and pasting in a specific incantation of lines designed to get different formats of fonts working in different browsers, and may not have really explored all the capabilities of @font-face properties as defined by the spec.
One such property — the unicode-range descriptor — sounds pretty dull and is easily overlooked. It does, however, have some fairly interesting possibilities when put to use in creative ways.
Unicode-range
The unicode-range descriptor is designed to help when using fonts that don’t have full coverage of the characters used in a page. By adding a unicode-range property to a @font-face rule it is possible to specify the range of characters the font covers.
@font-face {
font-family: BBCBengali;
src: url(fonts/BBCBengali.ttf) format("opentype");
unicode-range: U+00-FF;
}
In this example, the font is to be used for characters in the range of U+00 to U+FF which runs from the unexciting control characters at the start of the Unicode table (symbols like the exclamation mark start at U+21) right through to ÿ at U+FF – the extent of the Basic Latin character range.
By adding multiple @font-face rules for the same family but with different ranges, you can build up complete coverage of the characters your page uses by using different fonts.
When I say that it’s possible to specify the range of characters the font covers, that’s true, but what you’re really doing with the unicode-range property is declaring which characters the font should be used for. This becomes interesting, because instead of merely working with the technical constraints of available characters in a given font, we can start picking and choosing characters to use and selectively mix fonts together.
The best available ampersand
A few years back, Dan Cederholm wrote a post encouraging designers to use the best available ampersand. Dan went on to outline how this can be achieved by wrapping our ampersands in a <span> element with a class applied:
<span class="amp">&</span>
A CSS rule can then be written to select the <span> and apply a different font:
span.amp {
font-family: Baskerville, Palatino, "Book Antiqua", serif;
}
That’s a perfectly serviceable technique, but the drawbacks are clear — you have to add extra markup which is borderline presentational, and you also have to be able to add that markup, which isn’t always possible when working with a CMS.
Perhaps we could do this with unicode-range.
A better best available ampersand
The Unicode code point for an ampersand is U+26, so the ampersand font stack above can be created like so:
@font-face {
font-family: 'Ampersand';
src: local('Baskerville'), local('Palatino'), local('Book Antiqua');
unicode-range: U+26;
}
What we’ve done here is specify a new family called Ampersand and created a font stack for it with the user’s locally installed copies of Baskerville, Palatino or Book Antiqua. We’ve then limited it to a single character range — the ampersand. Of course, those don’t need to be local fonts — they could be web font files, too. If you have a font with a really snazzy ampersand, go for your life.
We can then use that new family in a regular font stack.
h1 {
font-family: Ampersand, Arial, sans-serif;
}
With this in place, any <h1> elements in our page will use the Ampersand family (Baskerville, Palatino or Book Antiqua) for ampersands, and Arial for all other characters. If the user doesn’t have any of the Ampersand family fonts available, the ampersand will fall back to the next item in the font stack, Arial.
You didn’t think it was that easy, did you?
Oh, if only it were so. The problem comes, as ever, with the issue of browser support. The unicode-range property has good support in WebKit browsers (like Safari and Chrome, and the browsers on most popular smartphone platforms) and in recent versions of Internet Explorer. The big stumbling block comes in the form of Firefox, which has no support at all.
If you’re familiar with how CSS works when it comes to unsupported properties, you’ll know that if a browser encounters a property it doesn’t implement, it just skips that declaration and moves on to the next. That works perfectly for things like border-radius — if the browser can’t round off the corners, the declaration is skipped and the user sees square corners instead. Perfect.
Less perfect when it comes to unicode-range, because if no range is specified then the default is that the font is applied for all characters — the whole range. If you’re using a fancy font for flamboyant ampersands, you probably don’t want that applied to all your text if unicode-range isn’t supported. That would be bad. Really bad.
Ensuring good fallbacks
As ever, the trick is to make sure that there’s a sensible fallback in place if a browser doesn’t have support for whatever technology you’re trying to use. This is where being a super nerd about understanding the spec you’re working with really pays off.
We can make use of the rules of the CSS cascade to make sure that if unicode-range isn’t supported we get a sensible fallback font. What would be ideal is if we were able to follow up the @font-face rule with a second rule to override it if Unicode ranges aren’t implemented.
@font-face {
font-family: 'Ampersand';
src: local('Baskerville'), local('Palatino'), local('Book Antiqua');
unicode-range: U+26;
}
@font-face {
font-family: 'Ampersand';
src: local('Arial');
}
In theory, this code should make sense for all browsers. For those that support unicode-range the two rules become cumulative. They specify different ranges for the same family, and in WebKit browsers this has the expected result of using Arial for most characters, but Baskerville and friends for the ampersand. For browsers that don’t have support, the second rule should just supersede the first, setting the font to Arial.
Unfortunately, this code causes current versions of Firefox to freak out and use the first rule, applying Baskerville to the entire range. That’s both unexpected and unfortunate. Bad Firefox. On your rug.
If that doesn’t work, what can we do? Well, we know that if given a unicode-range Firefox will ignore the range and apply the font to all characters. That’s really what we’re trying to achieve. So what if we specified a range for the fallback font, but made sure it only covers some obscure high-value Unicode character we’re never going to use in our page? Then it wouldn’t affect the outcome for browsers that do support ranges.
@font-face {
font-family: 'Ampersand';
src: local('Baskerville'), local('Palatino'), local('Book Antiqua');
unicode-range: U+26;
}
@font-face {
/* Ampersand fallback font */
font-family: 'Ampersand';
src: local('Arial');
unicode-range: U+270C;
}
By specifying a range on the fallback font, Firefox appears to correctly override the first based on the cascade sort order. Browsers that do support ranges take the second rule in addition, and apply Arial for that obscure character we’re not using in any of our pages — U+270C.
So we get our nice ampersands in browsers that support unicode-range and, thanks to our styling of an obscure Unicode character, the font falls back to a perfectly acceptable Arial in browsers that do not offer support. Perfect!
That obscure character, my friends, is what Unicode defines as the VICTORY HAND.
✌
So, how can we use this?
Ampersands are a neat trick, and it works well in browsers that support ranges, but that’s not really the point of all this. Styling ampersands is fun, but they’re only really scratching the surface. Consider more involved examples, such as substituting a different font for numerals, or symbols, or even caps. Things certainly begin to get a bit more interesting.
How do you know what the codes are for different characters? Richard Ishida has a handy online conversion tool available where you can type in the characters and get the Unicode code points out the other end.
Of course, the fact remains that browser support for unicode-range is currently limited, so any application needs to have fallbacks that you’re still happy for a significant proportion of your visitors to see. In some cases, such as dedicated pages for mobile devices in an HTML-based phone app, this is immediately useful as support in WebKit browsers is already very good. In other cases, you’ll have to use your own best judgement based on your needs and audience.
One thing to keep in mind is that if you’re using web fonts, the entire font will be downloaded even if only one character is used. That said, the font shouldn’t be downloaded if none of the characters within the Unicode range are present in a given page.
As ever, there are pros and cons to using unicode-range as well as varied but increasing support in browsers. It remains a useful tool to understand and have in your toolkit for when the right moment comes along.


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.
01/12/2011
It’s both actual and expected.
Yes, that would be ideal where possible.
Vote Helpful or Unhelpful
01/12/2011
John Fish: I believe opentype files can have a .ttf extension if the fonts contained are TrueType. OpenType is just a container format.
Vote Helpful or Unhelpful
01/12/2011
Webkit is super smart with unicode-range. It’ll only download the ampersand font if you use an ampersand. Because it gives the browser advance knowledge of the usable chars in the font, webkit will download fonts in parallel rather than series.
Vote Helpful or Unhelpful
01/12/2011
Brilliant piece, Drew!
Regarding unicode range and subsets: At Fontdeck we have tiny fonts we call ‘expert subsets’ containing only OpenType features like swashes for some faces like Pimlico and Trilogy. Firefox 4 supports CSS 3 OpenType features. For the rest, specific characters can be displayed using OpenType alternates, swashes, or anything else, using regular HTML hooks. However, this is better way if the fine folks at Mozilla can implement it.
Rich Rutter has described subsets as a modular approach. I agree, and refer to them already as modular font stacks. Including special features in small files that can progressively enhance the type, perhaps selected with this method, makes a lot of sense for those of us concerned with performance and extensibility.
Vote Helpful or Unhelpful
01/12/2011
Hi Drew, fab article to kick off this year’s 24ways! I didn’t know you could do that with unicode range!
I know that ampersands were just the example not your necessary intention, however I though that was a really good use of it and have implemented better ampersands on Lanyrd now:
An Example, The Art & Science of Javascript
We are using Eigerdals (through Fontdeck) for the heading typeface, which I adore, but I have never been keen on the ampersand, it looked a little too much like a ligature for Et. So using the technique you demonstrated I have switched to Hoefler Text just for the ampersands.
I discovered that the ampersands I wanted to use were all in the italic font and naturally I wanted my titles not to be. So I used the ‘PostScript Name’ of the individual font style I wanted to use rather than the family, and this worked.
Our stack for example is:
src: local(‘HoeflerText-BlackItalic’), local(‘AGaramondPro-BoldItalic’), local(‘MinionPro-BoldIt’), local(‘AdobeHebrew-BoldItalic’);
I deliberately only have fallbacks that looked as I wanted them, because if it cant find one of those it falls back to Eigerdals. For Firefox I gave the @font-face rule the same fallback stack as I had for the title text originally.
Vote Helpful or Unhelpful
02/12/2011
Instead of using that hideous conversion website, here’s a little javascript snippet that will convert a character to its unicode hex equivalent
Vote Helpful or Unhelpful
01/12/2011
If you enjoyed the post, I’d recommend checking out Font Face – Good vs Legal by Jake Archibald at DIBI last year
It’s a bit more in depth, but Jake keeps it interesting and entertaining.
Vote Helpful or Unhelpful
01/12/2011
Have you checked that this issue is logged as a bug in bugzilla? Given that this is a quite a niche use (at least at present), it’s not unlikely that the Firefox developers aren’t even aware of this issue.
Also, you say that “the font shouldn’t be downloaded if none of the characters within the Unicode range are present in a given page.” Is that the actual behaviour, or is that just the expected behaviour?
Would an alternative fallback be to strip down the font (using, say, FontForge) so that it only includes the characters to be substituted? I understand that some fonts’ licensing and distribution methods preclude this, but for other fonts, it could potentially mean less fallback CSS and smaller font downloads.
Vote Helpful or Unhelpful
01/12/2011
Very interesting article, I might use this. Just to be a nitpicker though, in the first CSS example you gave, the file extension was .ttf, however you gave it a format of opentype (rather than truetype). However, great thoughts, and really looking forward to the rest of the 24 ways this year :)
Vote Helpful or Unhelpful
01/12/2011
Great article, I had no idea it that was possible. I just hope it doesn’t get taken to the extreme, a different font per character… urghh! There’s always a client somewhere who will want it.
Vote Helpful or Unhelpful
01/12/2011
For those wondering about Firefox … unicode-range bug
Vote Helpful or Unhelpful
01/12/2011
Awww :3 There we are again. Great to have you here on the worlds most brilliant advent calendar.
Very interesting article, as ever.
Used @fontface a lot and never knew unicode-range existed. Thank you!
How do I declare multiple characters?
unicode-range: character, character, character; ?
A new fontface rule for every special character seems a bit dull, especially, when they’re nested in the same font ö.ö
Vote Helpful or Unhelpful
01/12/2011
It would be nice to hear which has the widest compatibility and best server / client side performance for those of us running big sites. <span> vs UTF. Thanks.
Vote Helpful or Unhelpful
01/12/2011
This is a lovely technique and in some ways is preferable to using span.amp, for example. However, I see one big flaw with this: how are you to style the ampersand itself?
For example, you may want the ampersand to be italic, it may have a different x-height so needs a different font-size and so on and so forth.
That being said, it’s extremely exciting.
Vote Helpful or Unhelpful
01/12/2011
Couldn’t you use a unicode range that excluded the ampersand with Arial, and list that before your ampersand font. That way the natural fallback would be Arial for everything?
Vote Helpful or Unhelpful
01/12/2011
Why not just use javascript to find all “&” and replace them with <span class=“ampersand”>&</span>?
At least this way, your CSS and HTML will be clean.
http://patrickhaney.com/thinktank/2008/08/19/automatic-awesompersands
Vote Helpful or Unhelpful
01/12/2011
Couldn’t you do the reverse – specify an everything-but-ampersand range for the body fonts and an underlying fancy one for the remaining ampersand?
Vote Helpful or Unhelpful
01/12/2011
I’ve seen folks using the icon fonts like Dingbats etc for stylish bullets and so forth and I gather you could use this Unicode trick to allow you to use various dingbats from various font faces.
One question about the implementation of the code. Does just loading a certain range of unicode characters lower the download from the entire font library? say you ref Mrs Eaves as a font-face but only want the ‘& does using this unicode statement limit the download to just the & character or is the whole Mrs. Eaves font downloaded and only the one character is rendered?
I have seen in the case of Google Fonts where you can just link to a smaller font file if you are going use certain characters and not the whole alphabet.
Vote Helpful or Unhelpful
02/12/2011
Really nice technique, I must admit I was completely unaware of unicode-range property – just implemented it in my current project. Thanks Drew!
Vote Helpful or Unhelpful
02/12/2011
Thanks Drew, great article. @Ryan Seddon – wow, that is a very handy JS snippet. Will use both, soon.
Vote Helpful or Unhelpful
01/12/2011
Be careful when stripping down fonts. A lot of them only give you a license to use them in their entirety. If you start editing the font, you start breaking their T&C’s.
Vote Helpful or Unhelpful
01/12/2011
In addition to Jake’s comment, you for non-webkit browsers you can use the Google Font API’s text= option to restrict the downloaded characters.
Vote Helpful or Unhelpful
01/12/2011
Excellent article Drew, a great start to the series.
FWIW:
With regards to the notion some people are passing round that Firefox is the new IE. This is utterly bonkers. None of the modern browsers being released now are “the new IE”. Not even IE.
At the rate FF releases are going it won’t be too long before unicode-range support lands. Not to mention for the time being font rendering is much better in FF than any other browsers (at least on windows) in terms of legibility and kerning which I think are more important than subsetting.
Compared to FF, IE9 has no text-shadow, flexbox, gradients, transitions, webworkers… the list goes on, but that doesn’t mean it’s a bad browser.
Vote Helpful or Unhelpful
01/12/2011
In theory, you could. That would mean scoping a range above and below the characters you wanted to exclude. Browsers don’t currently support multiple ranges properly, so each range would need its own @font-face rule to do so.
It works, but you end up with much more complex and hard to maintain rules.
Because ampersands are the example, not the end goal.
Vote Helpful or Unhelpful
01/12/2011
24ways starts off another year with a great post containing info that I can immediately put into practice.
Thanks Drew, you’ve just given me a solution to a problem that’s been buggin me for the entire year!
Vote Helpful or Unhelpful
02/12/2011
I’m a little confused by your write-up here, the behavior you describe is different from your example. In your example the @font-face rule containing ‘local(Arial)’ follows the one containing ‘local(Baskerville)’. This renders the same in Firefox 8, Safari 5, Chrome 17 and IE9, it always uses Arial. The behavior you describe (Firefox showing Baskerville) actually occurs if you use the reverse order than the one shown in your example. Here’s a testpage showing the variations.
The unicode-range descriptor is really intended to help better control load behavior for subsetted fonts designed to support a wide variety of scripts and languages. Especially for CJK fonts, these can be rather large. An author can include a set of subsetted fonts to support Japanese and via unicode-range assure that those fonts are only downloaded when Japanese characters are actually used on the page.
With multiple @font-face rules for the same style settings, the CSS3 Fonts spec defines how these rules are referenced:
So the last @font-face rule defined is the first one matched. The reason this is specified this way is so that an @font-face rule defined later can override a specific range. If the compounding worked the other way, a given @font-face rule that didn’t specify a unicode-range descriptor couldn’t be overriden by later rules.
This isn’t a case of Firefox not wanting to implement this feature, we’ve just been focused on better support for the rich typographic controls that are also part of CSS3 Fonts. Just a matter of priorities, we have every intention of implementing all features described in the spec.
As for “opentype” vs. “truetype” format hints, the spec defines them to be synonymous. That’s due to the fact that the terms are used ambiguously and OpenType is a superset of TrueType.
John Daggett
Mozilla Japan
CSS3 Fonts editor
Vote Helpful or Unhelpful
02/12/2011
Nice, but you end up mixing and matching fonts inline. Of course that’s still better than a height:1em PNG. But what I’d really like to see is a true fallback for missing glyphs.
Say I want to use the emoji characters and provide an emoji webfont for that range, but once the general font has native emoji glyphs, use those because they were designed to fit in with other text in that font.
Now the only way to achieve that is to measure the rendered characters in Javascript and guess if they are present in the font or just a placeholder. Or use webfonts for everything (because I know the range of the fonts I provide), but that’s a big overhead.
Vote Helpful or Unhelpful
03/12/2011
Special quotes characters could benefit from this unicode-range technique if used as a decoration elements around a citation with ::before and ::after pseudos.
Vote Helpful or Unhelpful
05/12/2011
@marc tamlyn
Testing on iOS5, src local seems to work just fine. Here’s an example test page.
The one caveat is that you need to use Postscript names, not fullnames (i.e. “Futura-MediumItalic” instead of “Futura Medium Italic”). If you’re using an OSX machine, open Fontbook, select a specific font, then select Preview > Show Font Info. Both the Postscript name and fullname are shown.
Vote Helpful or Unhelpful
08/12/2011
Couldn’t you just create a custom .ttf that only has the ampersand character in it? I vaguely recall some tutorials suggesting that in the past.
Vote Helpful or Unhelpful
11/12/2011
Does putting the unicode-range first help?
i.e.:
@font-face {
unicode-range: U+26;
font-family: ‘Ampersand’;
src: local(‘Baskerville’), local(‘Palatino’), local(‘Book Antiqua’);
}
Wouldn’t that then (in Firefox’s case) abort the @font-face rule parsing and thus no override the default font-face font the ampersand?
(Sorry I don’t have time to check this idea right now)
Vote Helpful or Unhelpful
01/12/2011
I find it immensely entertaining that Firefox, once a bastion of standards-compliance and the cutting-edge, has now started lagging behind Internet Explorer.
Vote Helpful or Unhelpful
01/12/2011
Very interesting article, thanks a lot.
I hope Mozilla is going to implement this property soon.
Vote Helpful or Unhelpful
01/12/2011
Great post, I’ve used the <span> method for ampersands before when using font-face. Will definitely give this method a go.
It does also raise the question, when is Firefox planning to support this? Does seem to be trailing behind somewhat lately with some people calling it the “new” IE.
Vote Helpful or Unhelpful
01/12/2011
Really great article to kick off this years series. I’m definitely going to keep the technique in mind during upcoming projects.
Vote Helpful or Unhelpful
01/12/2011
Brilliant!
I had no idea that something like this existed! Could be really useful for symbols.
With all the work that has been done on Internet Explorer lately, I’m a bit afraid that Firefox could become the new IE…
:-/
Vote Helpful or Unhelpful
01/12/2011
Very nice article, was fun to read! Now going to make all the ampersands shine in my projects… :-)
Vote Helpful or Unhelpful
01/12/2011
Love this technique, thanks for sharing. I can think of a few good uses for it.
Come on Firefox, pull your socks up!
Vote Helpful or Unhelpful
01/12/2011
Great article. I will not use this unicode-range technique for now, but I put the conversion tool in my bookmarks, very convenient.
Vote Helpful or Unhelpful
01/12/2011
Very interesting post to start with this year.
It is always regretful when a browser as popular as firefox doesn’t support it.
In the past IE had the most problems, but recently it becomes more and more firefox.
Vote Helpful or Unhelpful
01/12/2011
I’ve been waiting all year for these features and first has not disappointed – thanks for this and in anticipation of those to follow!
…and let’s not be too hard on FF; its achievements and driving influence on web design over the past few years have been prodigious.
Vote Helpful or Unhelpful
01/12/2011
Just a little tad of extra knowledge that can pull me out of a tricky challenge in the future. Thank you for sharing :)
Vote Helpful or Unhelpful
01/12/2011
Great article! I’ve only just discovered 24ways this year so looks like I’ve got some reading to do!
I’ve never played around with Unicode but it looks a great way to spruce up your websites font.
Vote Helpful or Unhelpful
01/12/2011
A bit over the top for me. I don’t know why I should be using this.
Typically I have a problem with fonts that they don’t support Spanish characters (ntilde, iacute, aacute, etc)
Are you saying I should use this (a range) to include a font that might look similar to the one that doesn’t support it to have some coverage?
Vote Helpful or Unhelpful
02/12/2011
I can’t believe some of the comments about ‘Firefox lagging behind IE’ and ‘Firefox becoming the new IE’.
…really guys?
Vote Helpful or Unhelpful
02/12/2011
Thanks, this is exactly what we needed!
On the subject of browser support – unicode-range works fine on iOS with but local doesn’t seem to work. Ended up just serving Symbol.ttf..
Vote Helpful or Unhelpful
02/12/2011
Great Article.
I’m going to try it out in a HTML e-mail I do for a client on a monthly basis. It contains lots of &‘s.
Place your bets for support in e-mail clients? Will keep you posted
Vote Helpful or Unhelpful
01/12/2011
Very good article, the first time i read about this unicode-range technique, learnd something today so thank you!
Vote Helpful or Unhelpful
Impress us