A suggested fix for the dreaded FOUT

As a keen typography fan I tend to rely heavily on the use of web fonts for my sites. I generally use Typekit to deliver my fonts, but it does tend to take a little while for the browser to download and display my beautiful fonts.

I noticed that if I simply place the typkit required script tags in the header of my site, it will block my site from loading until the font(s) have been downloaded, leaving visitors with a blank screen for a second or so. With a fast connection it’s not the end of the world in my book, but recently I tried experimenting a little with how to minimize this inconvenience to the user.

First, I tried putting the typekit script tags in the footer of my document, instead of the header. This stopped the delay in the site from loading, but it also caused a ‘Flash of Un-styled Text’ (or FOUT for short), where the fallback font(s) were shown as the web fonts were being downloaded. This can look really messy, especially if you’re using fonts for which there isn’t really any suitable fallback.

It’s a common problem and I’ve seen a couple of solutions offered, but I came up with what I think is quite an elegant solution.

Typekit fonts are delivered via JavaScript that provide event listeners that can tell you whether a font is active, is loading, is inactive or has been loaded. Typekit suggest a solution on their blog at http://blog.typekit.com/2010/10/29/font-events-controlling-the-fout/, but the loading of your site can still be delayed as you wait for the fonts to download and if you put the script tags in your footer, you can still suffer the FOUT. Blurgh.

My solution is to (and bear with me here before you scream ‘but what if JavaScript is disabled!’) hide all the text elements in my CSS by setting their opacity to 0, so as to completely avoid any FOUT. I do this like so:

h1, h2, h3, h4, h5, h6, p, ul, ol, dl, span, a {
opacity: 0;
-webkit-transition: opacity 0.4s ease-in;
-moz-transition: opacity 0.4s ease-in;
-o-transition: opacity 0.4s ease-in;
transition: opacity 0.4s ease-in;

Obviously you may well have more elements of text that should be hidden, such as blockquotes, cites etc. I’ve also added some CSS3 transitions, so that when the web fonts have finished loading they’ll fade in smoothly (this in itself is really quite a nice effect) rather than just being spat out! I then added the following to my CSS:

.wf-active h1, .wf-active h2, .wf-active h3, .wf-active h4, .wf-active h5, .wf-active h6, .wf-active p, .wf-active ul, .wf-active ol, .wf-active dl, .wf-active span, .wf-active a {
opacity: 1;

The .wf-active class is added to the <html> tag of my document via JavaScript when the font has finished loading. If we set the opacity here to 1 then our web fonts will be displayed and not our fallback fonts.

The problem with this method is obviously that it relies on JavaScript. If users view the site with JavaScript disabled, the wf-active class won’t be added to the <html> tag and the fonts will remain invisible. My solutions is to add the following to my document head after our other CSS files have been included.

<noscript><style>h1, h2, h3, h4, h5, h6, p, ul, ol, dl, span, a {
opacity: 1;
} </style></noscript>

This will overwrite our previous declaration where we set the opacity to 0 and therefore make our fallback fonts visible to the user without JavaScript.

The other problem with this method if the Typekit fonts are not available, or if the browser does not support web fonts. In this case, you could also use the .wf-inactive class which Typekit adds to the <html> tag in either of these cases and set the opacity to 1 like so:

.wf-inactive h1, .wf-inactive h2, .wf-inactive h3, .wf-inactive h4, .wf-inactive h5, .wf-inactive h6, .wf-inactive p, .wf-inactive ul, .wf-inactive ol, .wf-inactive dl, .wf-inactive span, .wf-inactive a {
opacity: 1;

As far as I can see this pretty much covers all bases, but I may well have missed something here and am very happy to have this pointed out to me!

One Reply to “A suggested fix for the dreaded FOUT”

Comments are closed.