How to use web fonts

The following is an excerpt taken from Bram Stein's Webfont Handbook. Buy it here.

Web fonts are defined in CSS through the @font-face rule. If you’re a web developer, you’ve most likely written, copied and pasted, or at the very least seen an @font-face rule. 

For the sake of completeness, though, let’s quickly run through a basic example:

@font-face {
  font-family: Elena;
  src: url(elena-regular.woff);
}

This creates a new web font family that can be referenced through the font-family or font shorthand property. But something’s missing here. When referencing a web font in a font stack, always make sure to include at least one fallback font in case the web font fails to load. 

Here, if Elena fails to load, the browser will fall back on the generic serif font family:

p {
  font-family: Elena, serif;
}

There’s more to fallback fonts, but for now, let’s keep our font stack simple by including only the generic serif and sans-serif font families.

Font Families

Creating a font family with multiple styles is accomplished by creating an @font-face rule for each style and using the same font-family name. The following @font-face rules create a family with a normal and bold style:

   @font-face {
     font-family: Elena;
     src: url(elena-regular.woff);
     font-weight: normal;
}
   @font-face {
     font-family: Elena;
     src: url(elena-bold.woff);
     font-weight: bold;
}

You can use this font family in your CSS by referencing the family name and weight in your selectors. This applies the regular style to paragraphs and the bold style to strong paragraphs:

p {
  font-family: Elena, serif;
}

p strong {
  font-weight: bold;
}

Besides font-weight, @font-face also accepts the font-style and font-stretch property descriptors, which define styles such as italic and condensed. All three property descriptors can be used to create a single font family with multiple styles. Theoretically, this lets you create a family containing 243 individual styles (nine font-weight values × three font-style values × nine font-stretch values).

In practice, however, you’re limited to 27 values, since some browsers don’t support font-stretch. Take a look at the table below to see which browsers you can use, and look here for more detailed information.

Click the icon in the top right to enlarge the image

With luck, the remaining browsers will implement the font-stretch property soon, and you will be able to use all 243 font classifications.

Font Formats

The src descriptor tells a browser where to get a font file. The previous examples used a single font format, but you’ll often see URLs to multiple font formats combined with format hints, which are appended after the URL using the format("value") syntax. 

Format hints tell the browser what the format of the font file at a given URL is.

@font-face {
  font-family: Elena;
  src: url(elena-regular.woff2) format("woff2"),
       url(elena-regular.woff) format("woff");
}

If you list multiple formats, modern browsers will pick the first format they support based on the format hint. Therefore, it’s important to list web font formats in the order of best compression to least. 

Even though format hints are optional, always include them – they let the browser know about the format without needing to download the font. For example, if a browser does not support WOFF2, but does support WOFF, it can skip the WOFF2 font file based on the format hint.

Browsers support several web font formats: OpenType (TrueType), EOT, WOFF, and WOFF2. Some browsers also support SVG fonts, but they’re deprecated and should no longer be used (and should not be confused with the new OpenType-SVG format). 

EOT, WOFF, and WOFF2 are technically not font formats. They are compressed OpenType files with varying degrees of compression. WOFF2 offers the best compression, followed by WOFF and EOT.

In researching coverage for all browsers, you may have come across something called the bulletproof @font-face syntax by Fontspring. 

The bulletproof syntax uses EOT, WOFF2, WOFF, raw OpenType, and SVG font files for maximum browser coverage:

@font-face {
  font-family: Elena;
  src: url(elena.eot?#iefix) format("embedded-opentype"),
       url(elena.woff2) format("woff2"),
       url(elena.woff) format("woff"),
       url(elena.otf) format("opentype"),
       url(elena.svg#elena) format("svg");
}

The first URL line might look a little odd to you. Versions of Internet Explorer 8 and below do not support the syntax for multiple font formats, and treat the entire value of the src property as the URL. 

The bulletproof syntax tricks Internet Explorer 8 and below into thinking that the remaining URLs are part of the fragment identifier of the first URL. Because fragment identifiers are ignored when downloading files, Internet Explorer 8 and below simply use the first URL. 

Browsers other than Internet Explorer will skip the line because they do not support EOT. 

The rest of the entries are what you would expect: font formats listed in order of preference.

But is the bulletproof syntax still relevant? No. In fact, I think it’s harmful. SVG fonts are deprecated and only supported by browsers that are no longer in use. 

Most websites support Internet Explorer 9 and up, yet the syntax lists EOT as the first preferred font format. Even though Internet Explorer 9 and up support WOFF, those versions will still download the EOT file, simply because it is listed first.

Because most websites no longer support old browsers, I highly recommend using a simplified syntax. This simplified syntax covers all modern browsers, as well as slightly older ones that are still in active use, such as Android 4.4 and earlier:

@font-face {
  font-family: Elena;
  src: url(elena.woff2) format("woff2"),
       url(elena.woff) format("woff"),
       url(elena.otf) format("opentype");
}

Even though older Android versions are still used, worldwide reliance on these browsers is rapidly dwindling. Soon you will probably be able to drop the raw OpenType format as well, and simplify the syntax even further:

@font-face {
  font-family: Elena;
  src: url(elena.woff2) format("woff2"),
       url(elena.woff) format("woff");
}

In this case, someone running an older browser will simply see your fallback fonts instead of the web font. That’s fine; they can still read the content in the fallback font.

There’s another possible value for the src descriptor. The local function takes the name of a local font family. If the font happens to be installed on the system, the browser will use that instead, thereby avoiding an extra download.

@font-face {
  font-family: Elena;
  src: local("Elena"),
       url(elena-regular.woff2) format("woff2"),
       url(elena-regular.woff) format("woff");
}

While this may seem like a great optimisation, nothing guarantees that the local font matches your web font. 

You may get a different version of the font, a font with different language support, or even an entirely different font. For that reason, I usually recommend not using the local function unless you find these downsides acceptable.

This is an excerpt from Bram Stein's Webfont Handbook, from A Book Apart. In it, he explores what to consider when selecting web fonts, how to use them effectively, and how to optimise for performance.

Related articles:

Thank you for reading 5 articles this month* Join now for unlimited access

Enjoy your first month for just £1 / $1 / €1

*Read 5 free articles per month without a subscription

Join now for unlimited access

Try first month for just £1 / $1 / €1

Bram is a de­vel­oper and prod­uct man­ager work­ing at Adobe Type­kit.