How to Improve Page Performance with a Font Loader

A big thanks to Jason Pamental for the inspiration to write this article. I’d never have considered the issue otherwise!

When was the last time you used Arial, Times New Roman, Helvetica or … shudder … Comic Sans in your web pages? Web fonts took too long to arrive but, once they did, we never looked back. Fonts are fun, (often) free and fast to implement:

@import url(http://fonts.googleapis.com/css?family=Ubuntu:300,300italic,400,400italic,500,500italic,700,700italic);

You can then use the font throughout your pages, e.g.

body {
	font-family: Ubunutu, sans-serif;
	font-weight: 400;
}

The fonts also work in mobile devices so users get a great experience in your Responsive Web Design.

Or do they?

After images, fonts are the normally the largest assets in your web page. The Ubuntu font above adds almost 250Kb to the page weight which is noticeable on slower mobile connections. Chrome, IE, Safari and Opera leave a blank space while the font is loaded so the page is unusable. Firefox and older versions of Opera show text in a fallback font then switch — known as a Flash of Unstyled Text (FOUT). Neither option is ideal.

We rarely worry about font weight problems and make excuses such as “it’s only an issue on the first page” or “many users will have the font cached”. We may omit lesser-used fonts; for example, removing most of the Ubuntu italic styles saves almost 40%. Few dare to take the obvious solution of using standard OS fonts — our clients and designers would never forgive us.

The JavaScript webfontloader

Fortunately, there is another option: the webfontloader. This JavaScript library can load fonts from Google, Typekit, Fonts.com, Fontdeck or your own server in the background once the page has loaded. The library itself adds a further 17Kb to the page but it is also downloaded as a background process.

To load the Ubuntu font set above, we create a global object named WebFontConfig which defines our fonts and settings then load the webfontloader itself:

var WebFontConfig = {
	google: {
		families: [ 'Ubuntu:400,300,400italic,300italic,500italic,500,700,700italic:latin' ]
	},
	timeout: 2000
};

(function(){
	var wf = document.createElement("script");
	wf.src = ('https:' == document.location.protocol ? 'https' : 'http') +
		'://ajax.googleapis.com/ajax/libs/webfont/1/webfont.js';
	wf.async = 'true';
	document.head.appendChild(wf);
})();

We can therefore determine whether none, some or all fonts are loaded depending on the device and bandwidth capacity. Ideally, we could use the Network Information API but browser support remains limited. Alternatively, note the timeout setting in WebFontConfig; if the font files require more than two seconds to download, the request is abandoned.

CSS Callbacks

The webfontloader applies class names to the html element during operation:

  • .wf-loading — all fonts have been requested
  • .wf-active — all fonts are available
  • .wf-inactive — none of the fonts could be loaded

Classes are also applied for individual fonts:

  • .wf-<familyname>-<fvd>-loading — a single font has been requested
  • .wf-<familyname>-<fvd>-active — a single font is available
  • .wf--<familyname>-<fvd>-inactive — a single font could not be loaded

where <familyname> is a sanitized version of the font name and <fvd> is a variation description such as i4 for 400 weight italic.

This permits us to switch fonts once they have downloaded — in the same way Firefox operates, e.g.

/* default OS fonts */
body {
	font-family: arial, sans-serif;
}

/* fonts now loaded */
.wf-active body {
	font-family: 'Ubuntu';
}

JavaScript Callbacks

Similar JavaScript callback functions can be defined in the WebFontConfig although there are fewer situations where this is useful, e.g.

var WebFontConfig = {
	google: {
		families: [ 'Ubuntu:400,300,400italic,300italic,500italic,500,700,700italic:latin' ]
	},
	timeout: 2000,
	loading: function() {},
	active: function() {},
	inactive: function() {},
	fontloading: function(familyName, fvd) {},
	fontactive: function(familyName, fvd) {},
	fontinactive: function(familyName, fvd) {}
};

For more information, refer to the webfontloader documentation.

Minimizing FOUT

The Flash of Unstyled Text is jarring if your fallback font is significantly different in style, weight or spacing to your web font. However, with a little experimentation you can adjust the fallback font, weights, line-heights and margins to ensure page elements remain in approximately the same location when the web font is loaded…

See the Pen How to use a font loader by Craig Buckler (@craigbuckler) on CodePen.

Click the TOGGLE FONT button to see the font-switching effect. The change isn’t completely unnoticeable but, importantly, the user wouldn’t lose their place if they’d started reading.

You can add a TOGGLE FONT button to any page to help you assess suitable fallback styles:

<button onclick="document.documentElement.classList.toggle('wf-active');return false;">toggle styles</button>

In summary: font use may be free, but try to minimize the cost to the user. If you’re loading a megabyte of font files, your lovingly-created RWD layout isn’t suitable for mobiles!

Free book: Jump Start HTML5 Basics

Grab a free copy of one our latest ebooks! Packed with hints and tips on HTML5's most powerful new features.

  • Tony Pirat Gustafsson

    I would probably just not include the font on mobile devices :) I would not use a font that is 250kb either – weither it’s from a CDN or not.

    • MattDiMu

      IMO not using the font on mobile devices should be the last resort. Compressed right (WOFF2 / WOFF) and using only the necessary fonts variations (why do u have to load “400,300,400italic,300italic,500italic,500,700,700italic:latin”?) the size shouldn’t be so large. Especially as the main problem on mobile devices is usually not the download size, but the amount of requests (combined with latency).

      • Craig Buckler

        And, how do you detect a mobile device? Screen-based media queries are too crude and user-agent sniffing is too problematic. You really need to check the network capacity – but the API to do that isn’t stable yet.

  • jokeyrhyme

    Do you need to change the `font-family` declaration after loading the font? Or would this work just as well by defining this in the usual way (order of most-preferred to least-preferred fallbacks)?

    Do web browsers not automatically re-evaluate the available fonts for CSS styles when new fonts become available (i.e. are loaded)?

    • Craig Buckler

      That’s the way Firefox works but the Webkit, Blink and Trident prefer to show nothing and wait for the font to be ready.

      • jokeyrhyme

        Ah. That’s why you do it this way. Okay, neat. :)

  • HenriHelvetica

    I was talking about this yesterday. Depending on the use of the font, i would try to limit the load. I was auditing a site just a few days past and client loaded an entire character set but was using 15 letters. Maybe he could shave some weight by streamlining his font request:

    text=MyText <— that would allow to determine which to use.

    via: http://googlewebfonts.blogspot.ca/2011/04/streamline-your-web-font-requests.html

    And of course, this is assuming this is part of the oh so critical css, in attempt to break that 1000ms mark.

  • Ross Z-Trigger Clutterbuck

    OK, forgive me for being a bit thick today (it’s been that sort of week) but how exactly does this font loader fix anything? The fundamental issue is the web font files haven’t downloaded for use, so how is loading up another 17K of script on top of the font request going to possibly solve anything?

    You’re going to get the FOUT one way or another, so really what is the point?

    Perhaps a better way of doing this is to consider the overall structure and progression through your site and try to mask the effects and hide the delay. For instance, throw up a splash screen or welcome panel that automatically triggers an advance once the web fonts have loaded (and a custom-written single check and callback for this single function won’t be 17K).

    But let’s be honest, this article plays up the issue with a crazy example of a million weights and styles for a font family. Do things properly and use only the weights, encodings and, hell, even the characters you need and nothing else. And also never forget that this is still the Web, not print, so should you really be loading up with multiple fonts in multiple weights?

    Oh, and don’t deliver web fonts to mobiles – easy.

    • Craig Buckler

      The point is: you will get FOUT, but it’s impact is minimized. That’s better than a jarring redraw or hiding the text for several seconds until the font is downloaded. In addition, the font won’t (fully) download or render on a slow connection.

      I don’t think this is a particularly crazy example. Four font weights are loaded with their corresponding italic versions. Ubuntu isn’t particularly heavy but it still adds up to 250Kb. Other fonts – especially handwriting and serif typefaces – add significantly more weight.

      You should only download the font weights you require. However, it’s important not to let the browser fall back its own weight rendering – it won’t always do a good job.

      Finally, do you really think a splash screen would be better solution? Sounds like an incredibly irritating sledgehammer to crack a nut! Give me FOUT any day!

      • Ross Z-Trigger Clutterbuck

        I think it’s a case of agree to disagree on this one. I’m just not convinced that adding another 17K of JS to a mobile’s download just to use some non-standard font is viable – sounds just as sledgehammery as my off-the-cuff suggestion of masking the FOUT with some form of cover mechanism, and even then I was still thinking Desktop if I’m honest.

        With all the talk and industry hype over Responsive Design and Mobile First, what’s so wrong with “Responsive Fonts”, ie don’t use web fonts on mobile?

        • Craig Buckler

          If you’re saying you only use standard OS fonts, that’s fine – you don’t need this solution and your site will be faster.

          However, if you’re saying you only want custom fonts on desktop, that’s a tougher prospect. Using media queries is too crude: you may be operating a mobile over wi-fi or a desktop on dial-up. You want to restrict font downloading when bandwidth is low … and that’s exactly what this solution does!

          • Ross Z-Trigger Clutterbuck

            True dat.

            I’ve had a few FOUT issues recently while my connection at home has been temperamental, so there will be experimentation on how to tackle it. Certainly this is one route I’m looking into.