The flash of nothing
Every browser picked a different answer and all of them are bad, so which ugly do you want?
Load a page on a slow connection and watch the text. Depending on your browser you’ll get one of two things.
Either the text is invisible for a while and then appears, which is a flash of invisible text, or it appears immediately in a fallback and then redraws in the real font, which is a flash of unstyled text. FOIT and FOUT, and everyone in this corner of the field has strong feelings about which is worse.
The thing I hadn’t grokked until recently is that this is a decision, and that if I don’t make it then the browser makes it for me, and different browsers make it differently.
Would you like the invisible or the unstyled?
Chrome and Safari block. Text using a downloading font is invisible, up to about three seconds, and then they give up and show the fallback. So on a fast connection you see nothing at all, and on a slow one you see nothing for three seconds, which on a phone on mobile data is a genuinely long time to look at a blank page that has already finished loading its layout.
Firefox blocks for about three seconds too, similar deal.
IE swaps immediately. Fallback first, real font when it arrives. Which everybody mocked for years and which, on a slow connection, means the reader can read.
So the default behavior on most of the web is: hide the content because we care about the typography more than the sentence. Put like that it’s a bit hard to defend, and I say that as someone who cares a lot about the typography.
The default on most of the web is to hide the content because we care about the typography more than the sentence.
Why FOUT is annoying anyway
The reason people prefer the blocking behavior isn’t vanity, or not only. It’s that the swap is jarring, and the reason it’s jarring is that the fallback and the web font have different metrics.
Different x-height, different character widths, different line height. So when the swap happens, the text reflows. Line breaks move. A paragraph gets a line shorter and everything below it jumps up. If someone’s mid-sentence, the sentence moves. If they were about to tap a link, the link is somewhere else.
That’s the actual cost, and it’s not aesthetic, it’s that you moved the page while somebody was using it.
Which suggests the fix isn’t picking FOIT or FOUT, it’s making the swap smaller. If you choose a fallback whose metrics are close to your web font, the reflow gets much less jarring. And you can go further and adjust the fallback’s size and spacing so it occupies nearly the same space, so the swap is a change of shape rather than a change of layout.
body {
font-family: Georgia, serif;
letter-spacing: 0.01em;
}
.fonts-loaded body {
font-family: "Whatever Pro", Georgia, serif;
letter-spacing: 0;
}
That’s fiddly and it’s per-font and it involves sitting there toggling a class and squinting. It also works and it only took me about half an hour, which is less time than I’ve spent thinking about it.
You get to choose
The way to actually control this, since the browsers won’t let you declare an intent, is to load the font with JavaScript and add a class when it’s ready.
You use a small loader script that resolves when the font has loaded, and it sets a class on the html element. Your CSS uses the fallback by default and the web font under that class. Now you’ve explicitly chosen FOUT everywhere, including in the browsers that would have blocked, and you decide the timing.
var font = new FontFaceObserver('Whatever Pro');
font.load().then(function () {
document.documentElement.className += ' fonts-loaded';
sessionStorage.fontsLoaded = true;
});
The sessionStorage line is the bit that made the biggest difference for me. On the second page view the font is already in the browser cache, so you can set the class immediately on page load and skip the swap entirely. Most visitors see the flash once per session at most, rather than on every navigation, and that turns out to be most of the perceived problem.
The unglamorous options
Two things that help more than any of the above, and neither is clever:
Load fewer fonts. Every weight and style is a separate file. A site with a regular, italic, bold, bold italic, and a display face at two weights is six requests before anybody reads anything. Most designs survive losing a weight, and faux-bolding a weight you don’t have is bad but not necessarily as bad as the sixth download?
And use WOFF2 where you can, with WOFF as the fallback. It’s meaningfully smaller, about 30% on the files I’ve measured, and support is decent now with WOFF covering everything older. There’s no reason to still be serving four formats with the bulletproof syntax from 2009 unless IE8 is in scope, in which case you need the EOT and you have my sympathies.
Give it a second
Self-host rather than use a font service, because it’s one fewer connection to a third party and I control the caching. Subset to the characters actually needed if the license allows for it. Two weights unless there’s a real argument for three. Fallback chosen for its metrics rather than because it’s the classic pairing. The loader script and the session storage trick.
And then accept a swap, once, on first visit, because the alternative is hiding the words from somebody who came here to read them.
I hold that last part more firmly than the rest of it. The typeface is a presentation choice and the text is the actual thing, and any technique that resolves in favor of the presentation when they conflict has got the priority backwards in my view. Which is easy to say and but, yes, it does mean watching your beautiful heading render in Georgia for 400 ms 🥲