Home > front-end development, typography > Fighting the @font-face FOUT - Quicken the load time

Fighting the @font-face FOUT - Quicken the load time

October 7th, 2009

FOUT is what I'm calling the flash of unstyled text that you get while using @font-face in Firefox and Opera.

In June, Remy Sharp documented the how a browser progressively renders a page using @font-face. Things work differently between browsers natch:

Here's how in Firefox; basically the text is in a default webfont until the custom font is ready:

Webkit takes a very different approach, and very intentionally. They believe it's better to keep the text invisible until the font is ready. This way, there is no moment where the text flashes into its newly upgraded self. (This moment should be familiar to you if you've used sIFR)

I really don't like the text upgrade FOUT, so I personally prefer webkit's technique. But either way, we want the font loaded ASAP, so let's speed it up!

Best practices for serving fonts:

  • Minimize the overall kilobyte size of your font file. You can reduce the size of your font file by subsetting it (more on this later).
  • Heavy caching via a far-future expires header.
  • Gzip? Well, no; you can't gzip a font file, though you can gzip a css file that holds the data-uri representation, but you don't get much gain there. It'd primarily be an obfuscation technique.Stoyan Stefanov has done some excellent research into @font-face and gzip. Summary: It's possible! 40-45% savings. Do It!

When exactly do browsers download the font file?

Garrick at Kernest tipped me off to IE's interesting behavior here.

After some research we can see when the asset download is initiated:

  • IE will download the .eot file immediately when it encounters the @font-face declaration.
  • No browsers download the font file when they find a css rule that references the @font-face font.
  • Gecko, Webkit, and Opera all wait until they encounter HTML that matches a CSS rule with a fontstack including the @font-face font.

I've put up a test page where you can experiment and watch your dev tools to see when the file is grabbed.

In what cases will you get a FOUT
  • Will: Downloading and displaying a remote ttf/otf/woff
  • Will: Displaying a cached ttf/otf/woff
  • Will: Downloading and displaying a data-uri ttf/otf/woff
  • Will: Displaying a cached data-uri ttf/otf/woff
  • Will not: Displaying a font that is already installed and named in your traditional font stack
  • Will not: Displaying a font that is installed and named using the local() location

Let's get the font ASAP

The sooner the better, so let's prioritize getting this font before everything else.

We'll set up our @font-face declaration:

    /* chunk will load immediately in IE at this declaration*/
    @font-face {
    	font-family: 'ChunkFive Regular';
    	src: url('fonts/Chunkfive.eot');
    	src: local('ChunkFive Regular'), local('ChunkFive-Regular'), url('fonts/Chunkfive.otf') format('opentype');
    }
    /* define a class that uses this font */
    .chunk { font-family:'ChunkFive Regular'}

And inside the <head>, we'll include this:

(function(className){
    // quit early if we're in IE, no need to do any of this.
    if (/*@cc_on!@*/0) return;
 
    var f = document.createElement('fontdl');
    f.innerHTML = 'fontdl';
    // associate with the @font-face declaration and hide it
    f.className = className;
    f.style.cssText = 'position:absolute; visibility:hidden'
    // it's still off-DOM so it doesn't download yet
 
    // document.body doesnt exist yet so we'll add it onto the HTML tag
    document.documentElement.appendChild(f);
    // font download initiated Now.
    // let's clean up after ourselves (opera needs a timeout > 0)
    setTimeout(function(){f.parentNode && f.parentNode.removeChild(f)},100)  
})('chunk'); // <== pass in the class here.
2009.10.10: Based on a tip from my colleague Adam McIntyre, we have a more elegant solution than the above javascript. It, along with more fontstack research, is as follows…

Rather than creating an element on the fly that uses the font, we do the same with HTML:
Adam postulated the we could do the same trick by classing the HTML tag:
(e.g. <html class="chunk">)

  • The font-family style won't actually cascade, so no worries about it being inherited by your content
  • This works in Gecko and Opera, but not Webkit

Alternatively, we add an applicable element to the head:
<b class="chunk" style="position:absolute; visibility:hidden">download please</b>

  • Works in webkit, gecko, opera. Waa hoo!
  • Obviously this doesn't validate. FYI, The element will be thrown into the <body> on page load.

Can I pre-load all the font assets?

So let's say we have more than one @font-face declaration:

@font-face { font-family: 'ChunkFive Regular'; src: local('ChunkFive Regular'), url('fonts/Chunkfive.otf') format('opentype'); }
 
@font-face { font-family: 'League Gothic'; src: local('league gothic'), url('fonts/LeagueGothic.otf') format('opentype'); }

If we set a new class that references both new fonts in a single fontstack, and then pass use that class for our above techniques:

  • Gecko retrieves all webfonts mentioned in the font stack.
  • Opera retrieves all webfonts mentioned in the font stack.
  • IE, as mentioned, retrieves them when they're declared, not used.
  • Webkit retrieves each font mentioned sequentially until it finds a working one.
    • 404s and invalid files are considered non-working, of couse.

So with some CSS like so:

    .chunk { font-family:'ChunkFive Regular'}
    .league { font-family:'League Gothic'}
    .allfonts { font-family: 'ChunkFive Regular', 'League Gothic'; }
<!-- preloads both fonts in gecko and opera, webkit only gets the first -->
<b class="allfonts" style="position:absolute; visibility:hidden">download please</b>
 
<!-- preloads all the fonts in the fontstack in gecko, opera, and webkit -->
<b class="chunk" style="position:absolute; visibility:hidden">download please</b>
<b class="league" style="position:absolute; visibility:hidden">download please</b>

Is this the end-all be-all solution to quick load and FOUT?

Nope. As Jonathan Snook pointed out in the comments, these won't elimate seeing the fallback font FOUT in Gecko and Opera, they only prioritize the load of those fonts. As we know, browsers have a limit of concurrent connections, so we're using these tricks to get the fonts first in line.

Also, This is really only for the initial time, because after that, your far-futures expire header means the ttf stays cached locally, no more requests needed.

I'm not sure if we'll be able to use webkit's transparent font load in Gecko in any graceful way. (It's possible with a sniff and polling, but that seems like overkill) I'm also not sure if we'll get Gecko's load technique in Webkit, which would be optimal for slow/mobile connections. For the time being your time is best served getting the font size very small, gzipping it, prioritizing it first, and caching it for a while.

Defeat the Firefox FOUT entirely

A little bit ago, Typotheque posted a technique aiming to avoid the FOUT. Using jQuery, they hide the body on dom ready, and then reveal it at the window load event.

The posted technique doesn't work as:

  • It targets all users, but we should only tweaks things for Firefox 3.5+ users.
  • Users will actually see the text before it's hidden during at dom ready.
  • As was mentioned earlier, fonts are downloaded when text appears in the page that the font will apply to. Therefore, anything hidden with display:none will not request the font file.
  • Not everyone has jQuery, so let's go with something more general

The one serious caveat to this technique is: The page will not be visible until all content, iframes, remote scripts, fonts, and images are downloaded. for a maximum of three seconds (I added a three second bailout condition, read below.)

This should run in the <head> somwhere:

(function(){
  // if firefox 3.5+, hide content till load (or 3 seconds) to prevent FOUT
  var d = document, e = d.documentElement, s = d.createElement('style');
  if (e.style.MozTransform === ''){ // gecko 1.9.1 inference
    s.textContent = 'body{visibility:hidden}';
    e.firstChild.appendChild(s);
    function f(){ s.parentNode && s.parentNode.removeChild(s); }
    addEventListener('load',f,false);
    setTimeout(f,3000); 
  }
})();

First, we are detecting if we're firefox 3.5+ by seeing if -moz-transform is supported, which was added at the same time. We use visibility:hidden instead of display:none, so that the font will actually be requested, and we remove that style once the page has finished loading. We're hinging on window load to be our re-entry point, because as Steve Souders pointed out, "font files block the window’s onload event from firing in IE and Firefox, but not Safari nor Chrome."

I've also added a 3 second bailout condition; this means if the page has not completely loaded in three seconds, we're going to show it anyway. It's possible the font won't be ready, but unlikely, I believe. This aims to solve the issue Remy found with the Standards.next site. I wouldn't recommend it, but you can disable this behavior by commenting out the setTimeout line.

2009.11.07. Added the Defeat the Firefox FOUT section.

2009.11.08. Tweaked defeat FOUT code to have a 3 second bailout.

2009.12.14. Added the In what cases will you get a FOUT section.

Paul Irish front-end development, typography

  1. | #1

    I'm in Firefox 3.5 and I still see the text styled in the default font for a fraction of a section before the proper font is applied. I even tested your code on my own site and experienced the same problem.

  2. | #2

    @Jonathan Snook ,

    Hah yeah, I should make that more clear. :) This will help, but it can't guarantee no more FOUT.

    I'm working on duplicating the webkit technique inside gecko and presto, which will be a nice option for people that like that style. It'll basically use the @font-face feature detect and poll that until the font is ready, and then remove a color:transparent setting.

  3. | #3

    what happens in webkit when it thinks it can get the font, but while getting it, something goes wrong? like the font is corrupted or w/e…

    does it then show some sort of fallback or does the text never show up? :x

  4. bganicky
    | #4

    I call this FOUC as "Flash of Unfonted Content" :)
    As for the Firefox, there's a ticket in Bugzilla already: https://bugzilla.mozilla.org/show_bug.cgi?id=499292

  5. | #5

    @seutje
    I'm fairly sure you won't be stuck without text, so it'll fall back. Sadly they expose none of these events to the javascript, so we have no obvious way of knowing.

    @bganicky
    Very nice, thanks for the bug on this, Bohdan. I see Daggett claiming you can hook into a resize event, but I think he's imagining things. :)

  6. | #6

    I have to pitch in and say I absolutely, whole heartily disagree that FOUT is the wrong way about things. I appreciate you're not saying one is better than another, rather which you prefer.

    Here's one page that always catches me out - and because my connection is slow at the moment (crappy ISP) - and more often that not I just give up on the page because it doesn't come down:

    http://dl.getdropbox.com/u/43399/fout.png

  7. | #7

    @Remy Sharp
    Thanks dude; appreciate hearing your feedback. I think the best possible solution would take into considering a user's bandwidth and estimate of the page load time. So slow/mobile would get FOUT, and fast would not.
    If only such a trick was trivial do to.

  8. | #8

    Ahh, my experience is that, while initiating the download as early as possible will help, the FOUT (or FOFF - Flash Of Fallback Font - as I'm calling it ;) ) can only be totally eliminated by supressing the display of the page until you know that the @font-face font has totally propagated throughout the page.
    I am not saying I have figured out the best way to do that, just that it's my belief, based on dealing with a similar problem, that it will prove to be the only totally reliable way.
    Anyway: Excellent, thought-provoking work. Thanks to Garrick, too, for the insight.
    We're learning!

    Rich

  9. Boris
    | #9

    Note that the setTimeout is likely to be needed in Gecko too in the future in the original ("remove the node" technique), and the size of the timeout needed is likely to depend on the load on the machine, etc.

    If you want to ensure the insertion is "noticed", you probably need to flush layout after doing it.

  10. | #10

    Geez … all designers want is a wider selection of fonts … little did they realize they're heading for a minefield.

    Very interesting post (and so is Steve's).

    I giggled when I saw that 'fout' means 'mistake', in Dutch! Apropos :-p

    For me: I'm happily ignoring all the suggestions under 'best-practices'. I don't like sub-setting - I *might* need that glyph, one day. The other two, while I understand the 'why', are overkill IMO.

    I'm hoping that @font-face usage increases dramatically and that browsers-makers catch up, standardize and optimize - or fix - their handling of font embedding.

    Cheers

  11. | #11

    @stk
    I'm using FOFF (Flash Of Fallback Font) instead of FOUT because it amuses me that there is now a font format named WOFF. So we've got WOFF and FOFF.
    (It makes me loff, what can I say?) FOUT will probably win out, though.
    I'm just a curmudgeon at heart.
    Cheers, rich

  12. | #12

    If you use document.write in the head of the document, you can still have your pages validate:

    document.write('<b>:)</b>');
  13. | #13

    @Pete
    IMHO, document.write() is more trouble than it's worth and certainly shouldn't be used to just for validation purposes. Regardless, what you say is true. :)

  14. | #14

    Thanks for taking the jQuery idea I had and running with it. I'm very new to this, but one thing that I like about the way I coded this was that I didn't have to hide all the elements on the page. Maybe you don't either, but the caveat that "The page will not be visible until all content, iframes, fonts, and images are downloaded" does seems like a drawback.

    Although my jQuery example (here: http://boxcarpress.com/us/blog/2009/11/05/css-font-face-flickering-in-firefox-somewhat-solved/ ) made the whole disappear before fading in, in practice I'm only doing this to divs with the @font-face text. In other words, all the images are loading with content on the page, then the text fades in with the correct font in place. This gives the visitor time to look at content so that it doesn't seem like something is wrong. Also, on one page where I have a lot of images which will delay load time, I show a loading "spinner" gif to make it clear that more content is coming.

    I hope this helps implementation for people who are scared of not showing anything until everything is loaded.

  15. | #15

    @Harold Kyle
    Very nice. I like just targeting this technique to the elements containing @font-face text. I've updated my above code to reveal all content after three seconds, so that loooong loading pages don't make users wait for ages. Good for people on slow connections.

  16. Boris
    | #16

    Of course if some future version of Firefox happens to change the behavior here you're still going to stick the user with that 3-second wall of nothing….

  17. | #17

    @Boris
    Yeah…. Definitely is a huge risk. I'm not ready to recommend people do this, but it's there for the anal folk that detest the FOUT.
    If there was an event to reliably detect when the font has loaded we would have less of a problem, of course.

  18. | #18

    @Boris In my jQuery js, just make the first line:
    if(($.browser.mozilla) && ($.browser.version < '1.9.2')){
    …which would limit this to versions prior to FF 3.6. The point is you may limit the scope however you may like (although FF 3.6 beta and 3.7 pre-alpha still show FOUT). There is no perfect way to load the page, but this is a slight improvement over the text re-formating in front of every Firefox user (to me anyway).

  19. snlr
    | #19

    doesn't work for me

  20. | #20

    You should check out TTFGen. http://www.ttfgen.com. It auto generates images from TTF files and CSS parameters. there's also a wordpress plugin…

  21. | #21

    Maybe I'm smoking crack, but there may be two (only) slightly more elegant ways of handling this:

    Use font-size-adjust. It won't stop FOUT, but it will make it much less jarring to the user. I haven't tested FF's implementation, so I don't know how reliable it is. I also don't know how well it's supported in other browsers.

    Use Javascript to detect the actual font-family applied to a chosen element. Honestly, I have no idea if this will work, but it's something I've been meaning to try.

    Ideas:

    Test the theory. See if Gecko accurately reports the *actual* font-family applied to the element, regardless of what it intends to do once the first-priority font is downloaded. I assume this is true since Firebug can accurately tell me the effective font-family for any element.

    Proposed test:

    Configure development web server to severely limit bandwidth for fonts.
    Clear browser cache.
    Run test page with similar JS w/ jQuery (I know you want a more general solution than jQuery, but I'm brainstorming here :)) and watch the fonts and console closely:

    (function() {
      var element = $("#someid"); // Grab an element to inspect.
      var timeout = (new Date()) + (30 * 1000); // Don't let this run forever.
     
      var timeout_id = window.setTimeout(function() {
        console.debug("Font family of " + element.selector + ": " + element.css("font-family"));
        if ((new Date()) > timeout)
          window.clearTimeout(timeout_id);
      }, 1000); // Run every second.
    )(); // Run it anonymously.

    If Gecko works like we expect:

    Use some JS w/ jQuery (similar to the above) like this:

    (function() {
      if ($.browser.mozilla && ($.browser.version > 1.9.1)) {
        var element = $("#someid"); // Grab the element to inspect.
        var font    = "somefont"; // the font family you want
        var timeout = (new Date()) + (5 * 1000); // Give up after 5 seconds.
     
        // Set up your favorite hide-the-content-and-show-undefined-progress method here.
     
        var timeout_id = window.setTimeout(function() {
          if (
            ((new Date()) > timeout)
              ||
            (element.css("font-family") == font)
          ) {
     
            // Show the content here.
     
            window.clearTimeout(timeout_id);
          }
        }, 500); // setTimeout()
      } // if (gecko)
    }(); // Run it anonymously.
  22. | #22

    Instead of adding an invalid element to the head or using JS to do the same thing, would the same technique work simply by applying a font-family rule to an existing element in the head?

    title {font-family: 'ChunkFive Regular';}

  1. | #1
  2. | #2
  3. | #3
  4. | #4
  5. | #5
  6. | #6
  7. | #7
  8. | #8
  9. | #9
  10. | #10
  11. | #11
  12. | #12
  13. | #13
  14. | #14

For code blocks, use <pre lang="javascript" escaped="true">. css and html4strict are also accepted.

Comments for this post will be closed on 2 October 2010.