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

Fighting the @font-face FOUT

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

Defeat the Firefox FOUT entirely #1: hide the page

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.

Defeat the Firefox FOUT entirely #2: hide the text

I posted this over on html5rocks.com's tutorial: Quick Guide to Implement Webfonts via @font-face.

Accompanying the Google Font API is the WebFont Loader, a javascript library aiming to provide a number of event hooks giving you a lot of control over the loading. Let's take a look at how you can get other browsers to mimic WebKit's behavior of hiding the fallback text while the @font-face font loads.

<script src="//ajax.googleapis.com/ajax/libs/webfont/1/webfont.js"></script>
<script>
WebFont.load({
  custom: {
    families: ['Tagesschrift'],
    urls : ['http://paulirish.com/tagesschrift.css']
  }
});
</script>
/* we want Tagesschrift to apply to all h2's */
.wf-loading h2 { 
  visibility: hidden; 
}
.wf-active h2, .wf-inactive h2 {
  visibility: visible; 
  font-family: 'Tagesschrift', 'Georgia', serif;
}

If JavaScript is disabled, the text will remain visible the whole time, and if the font errors somehow, it'll fall back to a basic serif safely. Consider this a stop-gap measure for now; most webfont experts desire hiding the fallback text for 2-5 seconds, then revealing it. Low-bandwidth and mobile devices benefit greatly by this timeout. Understandably, Mozilla is looking to rectify this soon.

A more lightweight but less effective solution is the font-size-adjust property, currently only supported in Firefox. It gives you an opportunity to normalize x-height across a font-stack, reducing the amount of visible change in the FOUT. In fact, the Font Squirrel generator just added a feature where it tells you the x-height ratio of the fonts you upload, so you can accurately set the font-size-adjust value.

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.

2010.05.03 font-size-adjust (only supported in Firefox) normalizes x-height and may improve the FOUT.

2010.05.26: I removed a bunch of old stuff from this article; it was kinda outdated.

2010.08.24: I updated this article with how to prevent FOUT using the Google Font API's WebFont Loader.

Take a look at some of my other (recently updated) webfont stuff:

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';}

  23. wes
    #23

    hey Paul, good read. you really went in deep with this issue.
    what about using the old staple of JavaScript preloading?? remember back in the day, preloading /choke/ rollover images?? since JS doesn't really know or care what it's downloading, theoretically can't you preload anything?? then it would be cached, I assume.

    var preloadedFont = new Image();
    preloadedFont.onload = function() {
        preloadedFont = null;
        // or delete preloadedFont; //... etc.
    };
    preloadedFont.src = 'font-file.ttf';

    haven't tested it, but seems logical, no??

  24. #24

    I realize that I am late to this party but this needs to be said, IMHO:

    If you prefer the Safari way of displaying nothing until fonts are downloaded - fine! Thats your choice and your taste.

    I think that getting to the content as quicky as posiible is better and wholeheartedly support Firefox's way of doing this. Although it can of course be improved. But until that happens, developers, please do not destroy my experience. Do not write scripts that will cause Firefox to mimick Safari. Respect your users!

    I am appalled to see that people still thinks that everything should "look the same" in every browsers and consider doing UA sniffing to achieve desired effects. Are we back in 1998?

  25. factotum
  26. #26

    i'm working on a site at dev.omsphoto.com, and put this code in the head.php file, and it shows up as text on the browser… is there something missing here? you can see it on the site now

  27. #27

    @bob bingenheimer
    The JavaScript should go inside <script> tags.

  28. porneL
    #28

    In text/html it's not possible to put "b" element in "head". Parser will close "head" and open "body" as soon as it sees "b", and then it may use error recovery to move some head-specific elements from body to head.

  29. Hawk
    #29

    "No browsers download the font file when they find a css rule that references the @font-face font."

    Something about that sounds wrong to me?

  30. #30

    @porneL
    Yeah. This behavior has seemed to work fine in all browsers. Similar thing with appending a b element to the documentElement or so.. But the technique seems to be okay here.

    @Hawk
    Nope. :)

  31. bit
    #31

    so your technique is to hide everything for whole 2 seconds because some text in the page doesn't look pretty for 2 seconds?
    way to prioritize!

  32. #32

    @bit - It's a matter of preference. In some instances one might prefer that delay instead of all of the page fonts shifting to their proper @font-face shortly after load. With some designs, that delay+shift looks awful, with others it's barely noticeable.

    Either way, it's really nice to have the option (thanks to Paul) to decide ourselves whether we'll have delay+shift or not. At the moment, it's the best solution.

  33. Matt
    #33

    @Jeff Couturier

    Personally, I think this is an amazing hack. Thanks for sharing, I'm definitely going to use it on sites where @font-face is crucial to the design and the flashing is very distracting.

  34. #34

    I just plugged in typekit's webfontloader to get tight JS control of font loading. Seems to work at this point, so I hope to be spared any further low-level load-time debugging!

    http://github.com/typekit/webfontloader

  35. #35

    Thanks, as usual great info.

    Big issue that is not mentioned, WebFont Loader does NOT support: iPhone, iPad, iPod, or Android. So the page renders only with the fallback font even though this devices are capable of rendering @font-face fonts (svg)

    http://github.com/typekit/webfontloader/issues/#issue/14
    http://code.google.com/apis/webfonts/faq.html#Browsers_Supported

  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
  15. | #15
  16. | #16
  17. | #17
  18. | #18
  19. | #19
  20. | #20
  21. | #21
  22. | #22
  23. | #23
  24. | #24
  25. | #25
  26. | #26
  27. | #27
  28. | #28
  29. | #29
  30. | #30
  31. | #31
  32. | #32
  33. | #33
  34. | #34
  35. | #35
  36. | #36
  37. | #37
  38. | #38
  39. | #39

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

i left this space here for you to play. <3