phantom underlines . isn't this amaaaaaazing.

i love waiting for 8 seconds and seeing this.

look at it. srsly. looooook at it.

I spent a week traveling around Taiwan, on my awesome free roaming 2G data plan, and friends, we need to talk about your web fonts. Also cats. They really love cats there. Anyway, the thing about 2G is that I fully understand that it will take me 10 seconds to load a page. What sucks is the fresh rage of the following 4 seconds where instead of content I get phantom underlines, waiting for a slightly-different-sans-serif to download.

Listen: it doesn’t have to be this way. You can lazy load your font. It’s 4 lines of JavaScript. 7 if you’re being ambitious.

Why should you care

I’ve been brainwashed to really care about first paint performance (thanks Chrome Dev Rel 😘), and I’ve become a big fan of the “do less & be lazy” approach to building things. What this means is that if something is not on your critical path, it probably doesn’t need to be the first thing you paint on a page.

Now think about fonts: is the critical path showing text, or styling it? I’d argue that unless your app is in the 1% it’s-all-a-magical-visual-experience bucket (in which case this post is not for you), or we’re just talking about the fancy title on your site (which fine, can be slow to paint or whatever), it’s probably trying to communicate some content, and ugly content (that you prettify after) is better than no content.

(Real talk: if you don’t think rendering text is a critical path, you’re whack and we need to have a chat.)

There are two things you can run into when loading a web font:

FOIC (“flash of invisible content”) – when your browser sees that you’re trying to use a font it doesn’t have it paints all the text in invisible ink, waits, and when it finally gets the font, it re-paints and re-layouts the text correctly. [see a gif of this]

I hate this with the fire of a thousand suns, because instead of looking at actual content, I’m looking at bullets and underlines and random text you forgot to style. Neat-o.

FOUC (“flash of unstyled content”) – Chrome stops waiting for a web font after 3 seconds (and, recently, after 0 seconds on 2G). What this means is instead of showing you invisible ink, it paints the text in your fallback font. When your web font is finally downloaded, it then re-paints the already displayed text with the new font. [see a gif of this]

Side note: on iPhones, this timeout doesn’t exist, so you basically only get a FOIC – you wait the entire time to get from “no text” to “all the text”, with no intermediate bail out state.

(Here is the code that I used for these demos, with GPRS and 2G throttling respectively in Chrome. This demo will look super snappy on LTE. Everything is super snappy on LTE.)

Reading material

A lot of people have written about web fonts, and I’m not trying to re-write their posts. Chrome in particularly has been working a lot on improving this, by decreasing the web font download timeout to 0s on 2G, and working on the font-display spec.

Here are some links I like:

the anatomy of a web font and the dance that a browser does to use a web font

font-display options, and how it affects how fonts load

options, and how it affects how fonts load font-display: optional and why it’s awesome (tl; dr: if you can’t do it fast, don’t do it at all)

and why it’s awesome (tl; dr: if you can’t do it fast, don’t do it at all) minimizing font downloads by limiting the range of characters you’re loading

why we should care about web fonts and how to minimize FOIT using JavaScript and a library called Font Face Observer

voltron solution combining FontFaceObserver, async loading a font bundle and web storage

Lazy loading a font

Personally, I would use font-display: optional everywhere, but that doesn’t really work anywhere yet. In the meantime, here are 2 super simple ways to lazy load a web font. Again, I don’t really mind having a FOUC, since it feels like progressive enhancement to me: display the content as soon as you can, and progressively style it after.

<head> <style> body { font-family : 'Arima Madurai' , sans-serif ; } </style> </head> <body> ... </body> <script> // Do this only after we've displayed the initial text. document . body . onload = function () { var url = ' https://fonts.googleapis.com/css?family=Arima+Madurai:300,400,500 ' ; loadFont ( url ); // hold tight, i tell you below. } </script>

There’s basically two ways in which you can implement that loadFont :

Load the stylesheet (blocking)

This is the simplest way and works great for a simple page. But! Since loading/parsing a stylesheet blocks parsing/painting, this doesn’t play nicely if you’re loading a bunch of other modules after the document has loaded, since they will be delayed. [demo]

var link = document . createElement ( ' link ' ); link . rel = ' stylesheet ' ; link . href = url ; document . head . appendChild ( link );

XHR the stylesheet (asynchronous)

If you care about synchronicity (and tbh you probably should), you can do an async XMLHttpRequest and create a style node with the result. [demo]

var xhr = new XMLHttpRequest (); xhr . open ( ' GET ' , url , true ); xhr . onreadystatechange = function () { if ( xhr . readyState == 4 && xhr . status == 200 ) { var style = document . createElement ( ' style ' ); style . innerHTML = xhr . responseText ; document . head . appendChild ( style ); } }; xhr . send ();

For bonus points, you can take this one step further and rather than creating an inline <style> , append a <link> like in the previous method, since the browser cache is already primed. If you trust your browser cache. I trust no one.

This is obviously not perfect. It will give you a FOUC on a fast LTE connection, even though if you did nothing, like in the first demo, it wouldn’t. The point is that not all of your audience is on an LTE connection, and I want you to think about them when you’re working on a site. If you want to minimize this FOUC, Helen Holmes gave an AMAZING talk recently about web typography and performance, where she mentions how you can try to match the x-heights of your fallback font to your target font, so that the FOUC is gentler.

Update: I’ve built a font-style-matcher that lets you do this matching of the x-heights and widths of the web font and fallback font! Go check it out, it’s preeeeetty sweet.

TL; DR

Web fonts are okay. They make your blog prettier. They’re also slow and kind of an annoying experience, but if you need to use them, use them. Just remember that it’s also your responsibility to make your site load super fast, and if you don’t, it’s totes fair game for people (me) to whine about it on Twitter.







(🍹 to Paul Lewis who had to sit through all my questions and explain basic browser things to me. Again.)