- phantom underlines. isn't this amaaaaaazing.
- i love waiting for 8 seconds and seeing this.
- look at it. srsly. looooookat 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 loadfont-display: optional
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.)