How to use CSS @font-face

Code up top for quick reference, details down below—we’ll prepare typefaces for use on the web, go through @font-face CSS line-by-line, and get the experts’ take on browser support. Updated May 2010 with new syntax from Paul Irish.

The CSS:

@font-face {
  font-family: "Your typeface";
  src: url("type/filename.eot");
  src: local("☺"),
    url("type/filename.woff") format("woff"),
    url("type/filename.otf") format("opentype"),
    url("type/filename.svg#filename") format("svg");
  }
@font-face {
  font-family: "Your italic typeface";
  src: url("type/filename-ital.eot");
  src: local("☺"),
    url("type/filename-ital.woff") format("woff"),
    url("type/filename-ital.otf") format("opentype"),
    url("type/filename-ital.svg#filename-ital") format("svg");
  }
h2 { font-family: "Your typeface", Georgia, serif; }
h2 em { font-family: "Your italic typeface", Georgia, serif; }
em { font-style: italic; }

The HTML:

<h2>This headline is typeset in <em>your typeface</em>.</h2>

Before we get started

If you want to brush up history and basics, Håkon Wium Lie has a great introduction to the CSS @font-face property in his A List Apart article, CSS @ Ten: The Next Big Thing. And as always, there are the getting started resources here at Nice Web Type.

Acquire a typeface

You may have a typeface in mind, or you may have no idea where to begin. Either way, read my post about Where to get web fonts. The type you already have may not be licensed right, and whether you’re looking to buy or seeking free fonts, you’ll want to know all of your options.

I have also experimented with several properly-licensed typefaces in a series I call Nice Web Type Likes, providing bits of advice with each example and explaining what I feel are each typeface’s merits.

For this demonstration I’ll use Fontin Sans from Exljbris. If you use it in your example, don’t forget to provide this line of attribution just above your @font-face declaration in the CSS:

/* A font by Jos Buivenga (exljbris) -> www.exljbris.nl */

Go grab a properly licensed typeface, then continue reading here.

Use several font formats

Once you have acquired a typeface, you’ll need to do one more thing: make alternate versions for broader browser support.

Most likely, the typeface you want to use is in one of the following formats: OpenType (OTF) or TrueType (TTF). However, Internet Explorer will only use Embedded OpenType, a format Microsoft has championed for years with little success. And on top of that, other browsers will only use Scalable Vector Graphics (SVG) fonts.

Lucky for us, creating alternate font files is much easier than it once was thanks to the generous genius of folks like Jonathan Snook and Font Squirrel’s Ethan Dunham.

Jonathan has published two excellent resources: a screencast that explains converting OTF or TTF to EOT, and a blog post that explains how to generate Scalable Vector Graphics (SVG) fonts using a command-line tool called Batik.

Font Squirrel offers an automated way to arrive at the same array of font formats: its excellent @font-face kit generator.

Font Squirrel's @font-face generator interface

This small, free web utility lets you upload a properly licensed typeface, then gives back the same typeface in many formats—plus some demo HTML/CSS files that show the font in action.

Worth noting: one of the generated formats is Web Open Font Format (WOFF), a proposed standard format for web-licensed typefaces. Including WOFF in our @font-face declarations now is a wise thing to do. Progressive enhancement, as they say.

Mac OS X Finder window with Font Squirrel @font-face kit contents shown.

Above, the files I received after having put Fontin Sans Regular and Fontin Sans Italic through Font Squirrel’s generator. Looking good.

Tools vs. “from scratch”

Whether to rely on handy services like Font Squirrel’s generator or roll our own solutions with guidance from smart folks like Snook? We’ll be faced with this decision until using real type in websites is as easy as, say, using images in websites.

As long as we understand how things work, and as long as we know what exactly our handy tools are generating, there’s no shame in using tools that make life easier. In fact, it’s just plain resourceful.

Get yourself organized

Having created versions of your typeface in a few different formats, make a few files and folders just for this tutorial. We’ll proceed as if your project is organized like so:

/My project
  /css
    /style.css
    /type
      /filename.eot
      /filename.otf
      /filename.svg
      /filename.woff
      /filename-ital.eot
      /filename-ital.otf
      /filename-ital.svg
      /filename-ital.woff
  /index.html

Make index.html really simple, like this (line wraps marked »):

<!DOCTYPE html>
<html>
<head>
  <title>Practicing @font-face</title>
  <link href="css/style.css" media="all" rel="stylesheet" »
      type="text/css" />
</head>
<body>
  <h2>This headline is typeset in <em>your typeface</em>.</h2>
</body>
</html>

Get down to business

Now let’s go through the CSS line-by-line and bookmark several excellent reference materials that explain the details thoroughly.

First of all, you may notice that this entire setup looks a little bulky. This is because we’re trying to reach (and avoid tripping up) as many browsers as possible. Go ahead and open up style.css.

Name that typeface
@font-face {
  font-family: "Your typeface";
  src: url("type/filename.eot");
  src: local("☺"),
    url("type/filename.woff") format("woff"),
    url("type/filename.otf") format("opentype"),
    url("type/filename.svg#filename") format("svg");
  }
@font-face {
  font-family: "Your italic typeface";
  src: url("type/filename-ital.eot");
  src: local("☺"),
    url("type/filename-ital.woff") format("woff"),
    url("type/filename-ital.otf") format("opentype"),
    url("type/filename-ital.svg#filename-ital") format("svg");
  }
h2 { font-family: "Your typeface", Georgia, serif; }
h2 em { font-family: "Your italic typeface", Georgia, serif; }
em { font-style: italic; }

Highlighted above are the font-family properties within our @font-face declarations, and matching names within our font stacks. These arbitrary names tie information about our typefaces (file names, formats, locations) to our font stacks, because we use the same name in both places.

Source sauce
@font-face {
  font-family: "Your typeface";
  src: url("type/filename.eot");
  src: local("☺"),
    url("type/filename.woff") format("woff"),
    url("type/filename.otf") format("opentype"),
    url("type/filename.svg#filename") format("svg");
  }
@font-face {
  font-family: "Your italic typeface";
  src: url("type/filename-ital.eot");
  src: local("☺"),
    url("type/filename-ital.woff") format("woff"),
    url("type/filename-ital.otf") format("opentype"),
    url("type/filename-ital.svg#filename-ital") format("svg");
  }

Highlighted above are the EOT versions of our typefaces which must be summoned first, via a separate src property than the others, for reasons Paul Irish explains in painstaking detail.

To paraphrase Paul’s explanation, the logic goes something like this. If a non-EOT file comes first, IE will load the non-EOT file even though it cannot be used, wasting bandwidth and time. Then, IE will choke on the format("whatever") part of the value and ignore the rest of the entire declaration.

All we need to do is put the EOT value first, right?

Not quite. Naturally, as per the CSS cascade, the declaration’s final src property takes precedence. So even though EOT comes first, IE still chokes. Internet Explorer, you magnificent bastard.

So what Paul has devised is a method of intercepting IE before it arrives at “problematic” formats. Using the best kind of CSS hack (one with no meaningless syntax), he diverts IE before it wastes bandwidth grabbing an unnecessary file—and before it chokes.

Think local, act local
@font-face {
  font-family: "Your typeface";
  src: url("type/filename.eot");
  src: local("☺"),
    url("type/filename.woff") format("woff"),
    url("type/filename.otf") format("opentype"),
    url("type/filename.svg#filename") format("svg");
  }
@font-face {
  font-family: "Your italic typeface";
  src: url("type/filename-ital.eot");
  src: local("☺"),
    url("type/filename-ital.woff") format("woff"),
    url("type/filename-ital.otf") format("opentype"),
    url("type/filename-ital.svg#filename-ital") format("svg");
  }

What Paul figured out is that IE chokes on not only local values, but multiple values of any kind within in a single src property.

Following Paul’s advice, our declaration’s final src property features a comma-separated list of values. In browsers other than IE, this series of values attempts to summon the typeface of your choice in several different ways, settling on whichever one works first (and skipping the rest).

Internet Explorer skips this whole line and sticks with the EOT file it has already loaded via our previous src property.

The local values tell browsers to check for your typeface on a visitor’s computer before attempting to load it from afar. Why have two when one will do? As Paul articulates, it’s because some browsers refer to full font names and other browsers to fonts’ PostScript names. John Daggett has tips on how to find these (scroll down to just below “Suffering succotash!”). Paul now recommends bulletproof smiley syntax, to circumvent a few different problems in browsers.

Our next three src values offer up WOFF, OTF, and SVG versions of your typeface. They’re listed in this order so that WOFF, and then OTF, take precedence.

Note that paths to SVG files include an ID. Here, #filename. If you convert your own SVG fonts, you define this value. If you use Font Squirrel’s generator, look at the demo CSS to find this value (for Fontin Sans Regular, I see this SVG ID: FontinSans-Regular).

And with that, you’re done. Preview index.html in a browser and see what you’ve got. Here’s my example using Fontin Sans.

Now don’t get too excited yet. There are some things left to consider, including browser support. Let’s keep talking about this while you still have the files open and are in the zone.

It takes two, baby

One of the most bothersome (but necessary) aspects of the syntax we’re using is that we define the roman and italic versions of our typeface as, technically, two different typefaces when they are actually two different styles of the same typeface.

@font-face {
  font-family: "Your typeface";
  src: url("type/filename.eot");
  src: local("☺"),
    url("type/filename.woff") format("woff"),
    url("type/filename.otf") format("opentype"),
    url("type/filename.svg#filename") format("svg");
  }
@font-face {
  font-family: "Your italic typeface";
  src: url("type/filename-ital.eot");
  src: local("☺"),
    url("type/filename-ital.woff") format("woff"),
    url("type/filename-ital.otf") format("opentype"),
    url("type/filename-ital.svg#filename-ital") format("svg");
  }
h2 { font-family: "Your typeface", Georgia, serif; }
h2 em { font-family: "Your italic typeface", Georgia, serif; }
em { font-style: italic; }

Above is our code. Below, see how it might look with “style linking,” a way of tying @font-face declarations to one another, and to font-style properties, similar to the way we connected values via font-family earlier in this tutorial.

@font-face {
  font-family: "Your typeface";
  src: url(type/filename.eot);
  src: local("☺"),
    url("type/filename.woff") format("woff"),
    url(type/filename.otf) format("opentype"),
    url("type/filename.svg#filename") format("svg");
  }
@font-face {
  font-family: "Your typeface";
  font-style: italic;
  src: url(type/filename-ital.eot);
  src: local("☺"),
    url("type/filename-ital.woff") format("woff"),
    url(type/filename-ital.otf) format("opentype"),
    url("type/filename-ital.svg#filename-ital") format("svg");
  }
h2 { font-family: "Your typeface", Georgia, serif; }
em { font-style: italic; }

See what changed? Instead of specifying a separate font-family name for the typeface’s italic style, we use the same name and connect it to the regular style via font-style style linking.

As a result, we no longer have to specify that em tags within h2 tags should use the italic typeface; by virtue of style linking and CSS inheritance combined, em tags behave as we would expect.

The reason we can’t use this more logical syntax today is because Internet Explorer will ignore it. None of our text would be italicized in IE. Also, Opera would look all messed up.

Browser support

There’s browser support, and then there’s browser support. So far we’ve channeled our inner Paul Irish just to get real type working in browsers that support @font-face!

What about browsers that don’t support it at all? What about older browsers, or some mobile browsers? Well, as per our CSS font stacks, such browsers would see Georgia. Or if they didn’t have Georgia, they’d see a default serifed typeface:

h2 { font-family: "Your typeface", Georgia, serif; }
h2 em { font-family: "Your italic typeface", Georgia, serif; }

Good enough?

If not, and if you would still like to use @font-face where it works, you might try isFontFaceSupported, a bit of JavaScript (again from Paul Irish—this guy is awesome) that checks for browser support. This kind of detection is also available in Modernizr, a JavaScript library from Faruk Ates that checks for all sorts of new features.

If you decide that font stacks are good enough, my Roundup: CSS font stacks post from April may be of interest to you. And although these are not perfect, in their Likes examples I suggest Helvetica to understudy Museo and Museo Sans, and a Lucida Sans stack to understudy Graublau Sans. Crud mitigation.

If you feel zen enough for font stacks but you want to provide slightly different typesetting instructions depending on typeface availability, see Matt Wiebe’s links near the end of our chat.

Optimization

Arguably part of browser support are subtleties like typeface file compression, flashes of unstyled text (FOUT) in some browsers and, in other browsers, @font-face only working if typeface files live at the domain where they’ll be used. I believe these have more to do with general web logic. Same goes for using @font-face with theme customization via services like Wordpress and Tumblr.

Such nuanced issues have everything to do with how files are transmitted and parsed, and they deserve a comprehensive post. Until I have such a post to share, here are some links:

Cross-Origin Resource Sharing
“Firefox 3.5 has set a precedent: it only fetches your web fonts, when linked from other sites, if you have expressly set your server to allow it.” This page from the Open Font Library explains how to set your server to allow it, via .htaccess, either in general or on a per-site basis.
Fighting the @font-face FOUT
Solutions from Paul Irish that tell Gecko and Opera to give font files high priority upon page load.
Steve Souders on @font-face and Performance
Steve Souders details the myriad performance issues and usability hurdles incurred by pages that use @font-face, then recommends a solution he calls, “Lazy Load.”
Gzip your @font-face files
Among the stats, bits of advice: “Always send your font files gzipped” and “keep an eye on the font file sizes you’re about to use, they can be huge.”
@font-face gzipping – take II
Stoyan Stefanov: “Basically, the tests confirm that WOFF is already compressed, it doesn’t need to be gzipped by the server and woff file sizes is comparable to the compressed TTF or OTF.”
Rendering Fonts in Today’s Browsers
Ryan Carver: “We recently rolled out an upgrade to that Javascript which improved compatibility, rendering perception and reduced the file size tenfold. There’s more to come – for example, we’re adding hooks which tell you when the fonts have loaded.”

The spec, the future

There are all kinds of @font-face goodies on our horizon, and it’s all because of forward-thinking people who make browsers, sell type, and care about design. Progress amid changing business.

CSS Fonts Module Level 3
The official W3C Editor’s Draft.
WOFF in Firefox 3.6
John Daggett of Mozilla announces upcoming WOFF support in Firefox and details differences between WOFF and TTF/OTF, with great examples.
New font control features for designers
Editable via CSS – OpenType features like discretionary ligatures, alternate glyphs, tabular figures, fractions, and language sensitivity. Mozilla’s John Daggett and Jonathan Kew are planning to extend the CSS font-variant property, beginning in Firefox 3.7.

Keeping up

Thanks for reading. One final note: stay abreast of web typography links like the ones cited here with Nice Web Type’s bookmarks. Also on the homepage and in the iPhone-friendly stream.

One more thing

Much respect for Jon Tan’s excellent post, Making Web Fonts Work. Published 364 days ago, this investigative, ahead-of-its-time post got lots of people thinking realistically about web fonts. Now Jon’s prescience fuels Fontdeck—can’t wait to see it.