Fighting the @font-face FOUT
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
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:nonewill 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.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.
Follow me on twitter: @paul_irish