Introduction to CSS3 Font-Face Design

Share this article

Web Fonts with @font-face

Since the early days of the Web, designers have been dreaming of creating sites with beautiful typography. But, as we know all too well, browsers are limited to rendering text in the fonts the user has installed on their system. In practical terms, this has limited most sites to a handful of fonts: Arial, Verdana, Times, Georgia, and a few others.

Over the years, we have come up with a number of clever workarounds for this problem. We created JPEGs and PNGs for site titles, logos, buttons, and navigation elements. When those elements required additional states or variants, we created even more images, or image sprites to ensure the page stayed snappy and responsive. Whenever the design or text changed, all those images had to be recreated.

This can be an acceptable solution for some elements on a page, but it’s just unrealistic to expect a designer to handcraft the title of every new article in Photoshop and then upload it to the site. So, for key page elements that need to change frequently, we were stuck with those same few fonts.

To fill this typographic void, some very cool font embedding scripts were created, like sIFR, based on Flash and JavaScript, and the canvas-based Cufón. While these methods have been a great stopgap measure, allowing us to include our own fonts, they had severe drawbacks. Sometimes they were tricky to implement, and they required that JavaScript be enabled and, in the case of sIFR, the Flash plugin be installed. In addition, they significantly slowed the page’s download and rendering.

Fortunately, there’s now a better way: @font-face is a pure CSS solution for embedding fonts—and it’s supported on every browser with any kind of market share, from IE6 on up.

Implementing @font-face

@font-face is one of several CSS at-rules, like @media, @import, @page, and the one we’ve just seen, @keyframes. At-rules are ways of encapsulating several rules together in a declaration to serve as instructions to the browser’s CSS processor. The @font-face at-rule allows us to specify custom fonts that we can then include in other declaration blocks.

To include fonts using @font-face, you have to:

  1. load the font file onto your servers in a variety of formats to support all the different browsers
  2. name, describe, and link to that font in an @font-face rule
  3. include the font’s name in a font-family property value, just as you would for system fonts

You already know how to upload a file onto a server, so we’ll discuss the details of the various file types in the next section. For now, we’ll focus on the second and third steps so that you can become familiar with the syntax of @font-face.

Here’s an example of an @font-face block:

@font-face {
  font-family: 'fontName';
  src: source;
  font-weight: weight;
  font-style: style;
}

The font family and source are required, but the weight and style are optional.

You need to include a separate @font-face at-rule for every font you contain in your site. You’ll also have to include a separate at-rule for each variation of the font—regular, thin, thick, italic, black, and so on.

The font-family part of the @font-face at-rule declaration is slightly different from the font-family property with which you’re already familiar. In this case, we’re declaring a name for our font, rather than assigning a font with a given name to an element. The font name can be anything you like—it’s only a reference to a font file, so it needn’t even correspond to the name of the font. Of course, it makes sense to use the font’s name to keep your CSS readable and maintainable. It’s good to settle on a convention and stick to it for all your fonts.

@font-face {
  font-family: 'LeagueGothic';
}

@font-face {
  font-family: 'AcknowledgementMedium';
}

Declaring Font Sources

Now that we have a skeleton laid out for our @font-face rules, and we’ve given each of them a name, it’s time to link them up to the actual font files. The src property can take several formats. Additionally, you can declare more than one source. If the browser fails to find the first source, it will try for the next one, and so on, until it either finds a source, or it runs out of options.

Let’s add more formats to our League Gothic declaration:

@font-face {
  font-family: 'LeagueGothicRegular';
  src: url('../fonts/League_Gothic-webfont.eot') format('eot'),
       url('../fonts/League_Gothic-webfont.woff') format('woff'),
       url('../fonts/League_Gothic-webfont.ttf') format('truetype'),
       url('../fonts/League_Gothic-webfont.svg#webfontFHzvtkso') format('svg');
}

There are four font sources listed in the code block above. The first declaration is an EOT font declaration, a proprietary format for Internet Explorer, and the only file type understood by IE4–8.

Then we define the WOFF (Web Open Font Format, an emerging standard), OTF (OpenType), TTF (TrueType), and SVG (Scalable Vector Graphics) font files. While most desktop browsers will use one of the first three declarations, be sure to include the SVG format, which was originally the only format supported by the iPhone.

Adding these extra font formats ensures support for every browser, but unfortunately it will cause problems in versions of IE older than IE9. Those browsers will see everything between the first url(' and the last ') as one URL, so will fail to load the font. At first, it would seem that we’ve been given the choice between supporting IE and supporting every other browser, but fortunately there’s a solution. Detailed in a FontSpring blog post, it involves adding a query string to the end of the EOT URL. This tricks the browser into thinking that the rest of the src property is a continuation of that query string, so it goes looking for the correct URL and loads the font:

@font-face {
  font-family: 'LeagueGothicRegular';
  src: url('../fonts/League_Gothic-webfont.eot?#iefix') format('eot'),
       url('../fonts/League_Gothic-webfont.woff') format('woff'),
       url('../fonts/League_Gothic-webfont.ttf') format('truetype'),
       url('../fonts/League_Gothic-webfont.svg#webfontFHzvtkso') format('svg');
}

This syntax has one potential point of failure: IE9 has a feature called compatibility mode, in which it will attempt to render pages the same way IE7 or 8 would. This was introduced to prevent older sites appearing broken in IE9’s more standards-compliant rendering. However, IE9 in compatibility mode doesn’t reproduce the bug in loading the EOT font, so the above declaration will fail. To compensate for this, you can add an additional EOT URL in a separate src property:

@font-face {
  font-family: 'LeagueGothicRegular';
  src: url('../fonts/League_Gothic-webfont.eot');
  src: url('../fonts/League_Gothic-webfont.eot?#iefix') format('eot'),
       url('../fonts/League_Gothic-webfont.woff') format('woff'),
       url('../fonts/League_Gothic-webfont.ttf') format('truetype'),
       url('../fonts/League_Gothic-webfont.svg#webfontFHzvtkso') format('svg');
}

This may be an unnecessary precaution, as generally a user would need to deliberately switch IE to compatibility mode while viewing your site for this issue to arise. Alternatively, you could also force IE out of compatibility mode by adding this meta element to your document’s head:

<meta http-equiv="X-UA-Compatible" content="IE=Edge">

It’s also possible to achieve the same result by adding an extra HTTP header; this can be done with a directive in your .htaccess file (or equivalent):

<IfModule mod_setenvif.c>
  <IfModule mod_headers.c>
    BrowserMatch MSIE ie
    Header set X-UA-Compatible "IE=Edge"
  </IfModule>
</IfModule>

Font Property Descriptors

Font property descriptors—including font-style, font-variant, font-weight, and others—can optionally be added to define the characteristics of the font face, and are used to match styles to specific font faces. The values are the same as the equivalent CSS properties:

@font-face {
  font-family: 'LeagueGothicRegular';
  src: url('../fonts/League_Gothic-webfont.eot');
  src: url('../fonts/League_Gothic-webfont.eot?#iefix')format('eot'),
       url('../fonts/League_Gothic-webfont.woff') format('woff'),
       url('../fonts/League_Gothic-webfont.ttf') format('truetype'),
       url('../fonts/League_Gothic-webfont.svg#webfontFHzvtkso') format('svg');
  font-weight: bold;
  font-style: normal;
}

Again, the behavior is different from what you’d expect. You are not telling the browser to make the font bold; rather, you’re telling it that this is the bold variant of the font. This can be confusing, and the behavior can be quirky in some browsers.

However, there is a reason to use the font-weight or font-style descriptor in the @font-face rule declaration. You can declare several font sources for the same font-family name:

@font-face {
  font-family: 'CoolFont';
  font-style: normal;
  src: url(fonts/CoolFontStd.ttf);
}
 
@font-face {
  font-family: 'CoolFont';
  font-style: italic;
  src: url(fonts/CoolFontItalic.ttf);
}
 
.whichFont {
  font-family: 'CoolFont';
}

Notice that both at-rules use the same font-family name, but different font styles. In this example, the .whichFont element will use the CoolFontStd.ttf font, because it matches the style given in that at-rule. However, if the element were to inherit an italic font style, it would switch to using the CoolFontItalic.ttf font instead.

Unicode Range

Also available is the unicode-range descriptor, which is employed to define the range of Unicode characters supported by the font. If this property is omitted, the entire range of characters included in the font file will be made available.
We won’t be using this on our site, but here’s an example of what it looks like:

unicode-range: U+000-49F, U+2000-27FF, U+2900-2BFF, U+1D400-1D7FF;

Applying the Font

Once the font is declared using the @font-face syntax, you can then refer to it as you would any normal system font in your CSS: include it in a “stack” as the value of a font-family property. It’s a good idea to declare a fallback font or two in case your embedded font fails to load.

h1 {
  text-shadow: #fff 1px 1px;
  font-family: LeagueGothic, Tahoma, Geneva, sans-serif;
  text-transform: uppercase;
  line-height: 1;
}

Our two embedded fonts are used in a number of different places in our stylesheet, but you get the idea.

Troubleshooting @font-face

If your fonts are failing to display in any browser, the problem could very well be the path in your CSS. Check to make sure that the font file is actually where you expect it to be. Browser-based debugging tools –such as the Web Inspector in WebKit, Dragonfly in Opera, or the Firebug Firefox extension — will indicate if the file is missing.

If you’re sure that the path is correct and the file is where it’s supposed to be, make sure your server is correctly configured to serve up the fonts. Windows IIS servers won’t serve up files if they’re unable to recognize their MIME type, so try adding WOFF and SVG to your list of MIME types (EOT and TTF should be supported out of the box):

.woff  application/x-font-woff
.svg   image/svg+xml

Finally, some browsers require that font files be served from the same domain as the page they’re embedded on.

This is an excerpt from HTML5 & CSS3 for the Real World, by Alexis Goldstein, Louis Lazaris & Estelle Weyl.

Adam RobertsAdam Roberts
View Author

Adam is SitePoint's head of newsletters, who mainly writes Versioning, a daily newsletter covering everything new and interesting in the world of web development. He has a beard and will talk to you about beer and Star Wars, if you let him.

Share this article
Read Next
Get the freshest news and resources for developers, designers and digital creators in your inbox each week
Loading form