Loading Web Fonts with the Web Font Loader

Several years ago the consensus on font loading in the community was that, as a website loads, all fonts should be hidden until the correct resources have been downloaded. Many designers and developers argued that the default font loading method called the “Flash of Unstyled Text”, or FOUT, was an annoyance to users. This is when the fallback web font, say Georgia, is shown on screen first then replaced by a custom font when it loaded. They argued that it would make for a more cohesive browsing experience if users simply waited for everything to download instead of experiencing this flash from one typeface to another.

But today this is not the case.

In fact, this practice of letting a browser hide all the text that should be styled with a custom font is now called the ‘Flash of Invisible Text’, or FOIT, and is often regarded as the worst of all possible options. Scott Jehl has argued that this is a pretty bad idea for both performance and usability:

The FOIT tends to be most problematic in browsers like iOS Safari, which hides text for up to 30 seconds before giving up and rendering it with a default font, but it can also be seen in browsers with shorter hiding durations like Chrome, Firefox, and Opera as well.

Likewise, the Typekit help page on the matter states that “the FOUT approach makes for more immediately usable pages, particularly on slower network connections.” So as designers and developers we have to make a decision between FOUT or FOIT.

Flash of Invisible Text

  1. Font starts downloading
  2. Text is invisible whilst web font is requested
  3. Time passes very slowly
  4. Web font loads
  5. Text with the webfont appears

Flash of Unstyled Text

  1. Font starts downloading
  2. Immediately show text with font-family fallback
  3. Web font loads
  4. Text with fallback font is replaced with the web font

The difference between these two approaches is often startling. Scott mentions that the FOIT approach caused the text on the Filament Group website to be visible in 2.7s on a 3G connection whereas the FOUT approach made the text visible in 0.6s. If we want to make our interfaces feel as fast as possible then we need to use the FOUT method; user experience and network performance is definitely the main priority with this technique.

Problems with the FOUT approach

This approach certainly has several disadvantages. For example, when the switch between fallback and fancy font occurs there can be an awful lot of juddering and jankiness due to differences in font-weight and x-height. Consequently a company may only want to communicate in a certain typeface for branding reasons but with this FOUT technique that’s obviously impossible.

Sometimes these disadvantages can be mitigated. Take a look for instance at Bram Stein’s website where the slight flash of unstyled text is instantaneous and the format of the page stays almost exactly the same before and after the loading has completed. Also, if we want to use a display typeface in which we can’t find a suitable fallback font then perhaps we can use SVG to display that text instead.

Walking through the FOUT technique

I’ve been experimenting with the Web Font Loader lately which gives developers a little more control as to how fonts are treated throughout the FOUT. First, we need to embed the Web Font Loader code into our markup:

(function(d) {
  var wf = d.createElement('script'), s = d.scripts[0];
  wf.src = 'https://ajax.googleapis.com/ajax/libs/webfont/1.5.18/webfont.js';
  s.parentNode.insertBefore(wf, s);
})(document);

This asynchronously loads the script onto the page, so you can add it just before the body ends, or in the head and the rest of the resources won’t be blocked. This technique is useful for the IE9 support, but if that’s not important for our project then we can use this method instead:

<script src="https://ajax.googleapis.com/ajax/libs/webfont/1.5.18/webfont.js" async></script>

Once we’ve got the script we can then add our fonts. In this side project I was experimenting with fonts that are served with @font-face from the Typonine foundry which we add as a link in the head of our markup:

<link rel="preconnect" href="https://fonts.typonine.com/" crossorigin>

preconnect is useful here because it will automatically make the network handshake for us; before we request the fonts from our script we already have all the information the browser needs to fetch those assets which will make that process just a little bit quicker. (Thanks to Ilya Grigorik for suggesting that we use the crossorigin attribute here as well.)

Now we can begin to check whether these fonts have loaded onto our page by using the WebFontConfig object:

WebFontConfig = {
  custom: {
    families: [
      'Nocturno Display Medium 3',
      'Nocturno Book 2',
      'Nocturno Regular Italic 24',
      'Nocturno Regular 26',
      'Nocturno Regular 25'
    ],
    urls: [
      'https://fonts.typonine.com/WF-000000-001254.css',
      'https://fonts.typonine.com/WF-000000-001255.css'
    ]
  },
  timeout: 2000
};

The custom object lets the Web Font Loader know that we want to load fonts from an external stylesheet, but with this loader we can use fonts from Typekit, Google, Fontdeck, or Fonts.com if we want to. With the families array we’re identifying all of the font-family names that we would otherwise use directly in the CSS.

You might notice that I’ve set a timeout of 2 seconds. That’s a completely subjective figure, but I think that’s plenty of time to make a request to the network—any longer than that and the user is probably on a poor connection and all they want is the content anyway.

Once the loader has finished with loading those fonts it will add these classes to the html element:

<html class="wf-nocturnoregularitalic24-n4-active wf-nocturnoregular26-n4-active wf-nocturnoregular25-n4-active wf-nocturnodisplaymedium3-n4-active wf-nocturnobook2-n4-active wf-active">
   <!-- markup for the website goes here -->
</html>

These classes are the hooks we need to style the page depending on whether the fonts have loaded or not. There are more classes that can be added to help us however:

  • loading: triggered when all fonts have been requested.
  • active: triggered when the fonts have rendered.
  • inactive: triggered when the browser does not support linked fonts or if none of the fonts could be loaded.
  • fontloading: triggered once for each font that’s loaded.
  • fontactive: triggered once for each font that renders.
  • fontinactive: triggered if the font can’t be loaded.

Over in our stylesheet, we can now set the right family with the .wf-active class:

$fallback: Georgia, serif;

h1, .h1 {
  font-family: $fallback;

  .wf-active & {
    font-family: "Nocturno Display Medium 3";
  }
}

So now our users will immediately see the fonts contained in the $fallback variable but once our .wf-active class is added by the Web Font Loader then those fonts will be switched out for the fancy web font. We’re now taking a progressive enhancement approach to font loading.

Avoiding the jank

One problem I noticed was that once these fonts had loaded then the number of words that fit onto a line changed and certain elements were too wide or too small because of these new additions to the page.

To fix this we can always set a different font-size and line-height combination to make this a smoother experience:

h1, .h1 {
  font-size: 30px;
  line-height: 35px;
  font-family: Georgia, serif;

  .wf-active & {
    font-size: 26px;
    line-height: 30px;
    font-family: "Nocturno Display Medium 3";
  }
}

Minimising the flash of unstyled text

After the first page load, The Filament Group recommends setting a cookie so that we don’t have that flash of unstyled text whilst the .wf-active class is applied to the markup each time. But in my experiment I was just using plain, static markup so the cookie option wasn’t really available. Instead, I used one of the many events available in the Web Font Loader to update sessionStorage, like this:

WebFontConfig = {
  // other options and settings
  active: function() {
    sessionStorage.fonts = true;
  }
}

Don’t forget to take a look at the full list of available events. But with this particular event we can immediately check in the head of the document whether that key exists in the sessionStorage and, if it does, we can then add the class name to the html element as quickly as possible:

<head>
  <script>
    (function() {
      if (sessionStorage.fonts) {
        console.log("Fonts installed.");
        document.documentElement.classList.add('wf-active');
      } else {
        console.log("No fonts installed.");
      }
    })();
  </script>
</head>

This doesn’t entirely block the flash of unstyled text which is probably going to be an issue either way, but it’s certainly an improvement on FOIT.

Font loading is crucial for good typography

After playing around with this technique I found that the experience completely changed my view about what good typography on the web is.

But if you happen to have any ideas on how to improve the method shown above then please let us know about them in the comments.