Using a web font loader

Orde Saunders' avatarby Orde Saunders

This is a walk through of how to get up and running with a web font loader to improve the user experience of your web pages when using custom fonts.

In this explanation we will be using Typekit's web font loader. This can be used to load fonts from a variety of sources, not just Typekit. I have used it on a number of client projects and it has proved useful and reliable and they provide good documentation.

What's the problem?

Web browsers operate in a fundamentally hostile environment, network requests can take time over slow connections (such as mobile networks) and it's not out of the ordinary for them to fail (such as a mobile network losing signal). If you are using custom fonts on your pages then the font has to be loaded over the network before it can be used so, until the font has loaded, the browser cannot display text in the custom font.

Showing text in a font that is available (such as a system font) before applying the custom font to the text when it has finished loading is known as a flash of unstyled text (FOUT). To avoid this FOUT (most) browsers do something that, to my mind, is much worse - they don't show any of the text that is to have the custom font applied to it. This essentially means that before your font loads your page will have none of the content destined to have the custom font - it's effectively unavailable.

The details of how browsers handle this situation vary: Internet Explorer will show the fall-back font immediately and then apply the custom font when it loads. Firefox won't display any content for the first few seconds, if the custom font hasn't loaded at that point it will show the system font. The worst offenders are WebKit based browsers (including Blink at this point) which will seemingly wait forever for the custom font to load and won't show any of the content destined to have the custom font until it loads (if it ever does).

An example of this is shown below - the only text showing on this page is the "gt" which is in a system font - all the rest of the text is hidden as the web font hasn't loaded yet.

Web font fail

To sum up, when (not if) there is a problem loading the font then your content won't be available. Not only that but, even if all goes well, there is a good chance that your page will show without visible content for a period of time before the font loads. We have potential issues with both fault tolerance and performance.

The solution

To circumvent browsers' default behaviour we use a JavaScript solution known as a web font loader. This essentially monitors the progress of web fonts loading and provides us with ways to react to the status.

How it works

When the web font loader is included and configured to watch your fonts it will set classes on the <html> element to reflect the current status.

There are three classes that refer to the general state of the loader:

  • .wf-loading
  • .wf-active
  • .wf-inactive

And three classes that refer to the state of each specific font:

  • .wf-[family name]-[variant]-loading
  • .wf-[family name]-[variant]-active
  • .wf-[family name]-[variant]-inactive

family name is the name of the font in lower case with no special characters.

variant is the variation description which relates to style and weight. The default for normal fonts is n4.

In practical terms, the best way to work out what class names are going to be set is to get the font loading using the font loader - then you can see the exact class names you need in the browser's dev tools inspector.

How to use it

If you are using hosted fonts provided by a third party service (such as Typekit or Google fonts) they should provide instructions on how to use their fonts with a font loader. However, the instructions given by these services are usually written to provide the easiest and safest way of including a font which frequently isn't the optimal way to include it.

For the rest of this explanation I will assume you are hosting the font files yourself, if you are using a hosted service then some of the details might change but the principles will remain the same.

The first step is to define the font in your CSS. An example of a CSS font definition is shown below, whilst this only uses WOFF format to save space you can define multiple formats to increase browser compatibility.

@font-face {
  font-family: 'Custom-Font';
  src: url('/path/to/Font-Name.woff') format('woff');
}

It's not uncommon to see font definitions shown in their own file that is included using a <link>. As CSS files included using <link> block rendering this is bad for performance. To prevent blocking rendering pending this extra request you should include the font definitions in your main CSS file, ideally at the top.

Next you will need to include the web font loader JavaScript. These instructions cover how to include this as a separate file, if you're using a build process I'll assume you know how to translate this into what you need.

Download a minified copy of the web font loader and, at the end of this file, add a magic spell to link it up with the fonts defined in your CSS. (This is the part that will differ if you are using hosted fonts.)

WebFont.load({
  custom: {
    families: ['Custom-Font']
  }
});

The key to the magic here is matching the definitions of families to the font-family property within the @font-face definition in the CSS file. In this example we have a CSS property of font-family: Custom-Font; so in the loader we also define Custom-Font.

If you were defining more than one font in your CSS you would need to change your families definition to include them as a list: families: ['Custom-Font', 'Second-Custom-Font', &hellip;].

You can now include the font loader script in the <head> of your page using an asynchronous script tag:

<script src="path/to/webfont.js" async></script>

At this point you should have everything you need in the page and you should see the classes appearing on the <html> element.

We can now add the font styling to our CSS. We start by defining the baseline font stack using only system fonts, this ensures that before the font loads we can still see the content.

body {
  font-family: Fall-back, font, stack;
}

We then use the class hook that tells us when the font has loaded to apply our custom font. As this will only apply after the font has loaded we know the content will be rendered immediately and the font will be applied only when it has loaded.

.wf-customfont-n4-active body {
  font-family: Custom-Font, Fall-back, font, stack;
}

One issue with the FOUT caused by this switching of fonts is they can - and frequently do - have different metrics, that's to say that the same text will take up different amounts of horizontal space. If the system font is replaced with a custom font with different metrics this can cause layout reflows as the space occupied by the text changes. To get round this we can use the class hooks to adjust the font metrics so they match as closely as possible. In the example below we are using a custom font that is narrower than the system font so we adjust the letter spacing of the system font so text takes up the same amount of space.

body {
  font-family: System-Font;
  letter-spacing: -0.015em;
}
.wf-customfont-n4-active body {
  font-family: Custom-Font, System-Font;
  letter-spacing: normal;
}

To help you match the font metrics, Paolo Ciarrocca has created a bookmarklet to match fonts.

Conclusion

By using a web font loader we can benefit from custom fonts whilst also avoiding situations where the default behaviour of browsers on less than ideal networks can leave our content unavailable. We benefit from an increase in perceived performance and fault tolerance.