Zach’s ugly mug (his face) Zach Leatherman

Lazy Loading Web Fonts Is Probably Not What You Want

November 16, 2016 On Twitter (archived)

One month ago Monica Dinculescu wrote a blog post entitled Web fonts, boy, I don’t know. In it, she writes:

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), 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.)

YES. Love it. 👍👍👍

And on the Flash of Invisible Text (FOIT), which she refers to as FOIC (they are synonymous):

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.

Yes again—kindred spirits here. We need to get web fonts off the critical rendering path.

I also love the nod to font-display (praise be to font-display) and Chrome for reducing their Flash of Unstyled Text (FOUT) timeout on 2G from 3 seconds to 0 seconds. In Monica’s post she refers to this as FOUC1 (again, synonymous).

Lots of great stuff in Monica’s post. However, in the introduction, she writes:

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.

Hmm, alarm bells are starting to go off. Before I continue on, I want to say that I have an incredible amount of respect for Monica’s work. She builds really great things. I don’t want this post to feel like I’m singling her out—she does amazing work. The reason I’m writing this post: I’ve seen this exact mistake made by many before.

One of the most confusing parts about web font loading is that the time in which the web font starts to download (and makes the text invisible using FOIT) requires more conditions to be satisfied than just a valid @font-face block. Whether or not a web font will download depends on a few things:

  1. The obvious, a valid @font-face block (with a src format that the browser supports)
  2. A node attached to the document that uses the same font-family.
  3. In WebKit and Blink, that attached node must also have content (not be empty).
  4. If the @font-face has a unicode-range descriptor (in browsers that support it), the content must fall inside the declared Unicode range.

The font-weight, font-style, and font-stretch descriptors inside the @font-face block do not need to match. But do note that if they don’t match you may see faux-bolding or faux-italic under certain conditions. Faux-bolding or faux-italic can be controlled with font-synthesis which is only supported in Firefox. So actually, when I said that it can be controlled—I meant that it can’t be controlled.

Once all of these conditions are met, the browser will start downloading your font and render any matching content invisible per the browser’s default FOIT. The only time you see invisible text on the screen is when the web fonts are downloading. The stylesheet/@font-face block is only one piece of the puzzle—and it only happens before a FOIT would ever begin.

So lazy loading your stylesheet (even if you do so asynchronously) only delays the initial time before your FOIT will begin. It doesn’t shorten the FOIT. It doesn’t minimize the time spent looking at invisible fonts. Lazy loading is actually much worse for readers than doing nothing—a reader could be a few paragraphs deep into your content before the text becomes invisible!

If you’re looking for a better web font loading solution, I’ve written a Comprehensive Guide to Font Loading Strategies which includes a bunch of different font loading approaches (that range from the simple to advanced). If you’re feeling overwhelmed, just go with the FOUT with a Class approach.

Addendum

  1. FOUC also refers to the scenario when your page renders before your CSS has successfully applied so I think it’d be better to stick with FOUT to avoid confusion. Jump to original reference.

A big thank you for Monica Dinculescu for reviewing this post before publication and giving her blessing to post it!


< Newer
faux-pas, Detecting Faux Web Font Rendering
Older >
Separating Fact from Fiction on the Internet

Zach Leatherman IndieWeb Avatar for https://zachleat.com/is a builder for the web at IndieWeb Avatar for https://cloudcannon.com/CloudCannon. He is the creator and maintainer of IndieWeb Avatar for https://www.11ty.devEleventy (11ty), an award-winning open source site generator. At one point he became entirely too fixated on web fonts. He has given 79 talks in nine different countries at events like Beyond Tellerrand, Smashing Conference, Jamstack Conf, CSSConf, and The White House. Formerly part of Netlify, Filament Group, NEJS CONF, and NebraskaJS. Learn more about Zach »

5 Reposts

IndieWeb Avatar for https://editor.sitepoint.comIndieWeb Avatar for https://editor.us-west-2b.sitepoint-staging.sitepoint.comIndieWeb Avatar for https://hashtagbeginner.comIndieWeb Avatar for https://itpcb.comIndieWeb Avatar for https://dev-hoangweb24.pantheonsite.io
3 Comments
  1. Paauł Kruczynski

    @paulkruczynski

    It may be. Reading these again in a new context, I might be using the wrong method on this project. I don’t have much control over the fonts. Just trying to get them to not use google fonts, but I may have to let it go

  2. Paauł Kruczynski

    @paulkruczynski

    Thank you for responding. It’s appreciated!

  3. Zach Leatherman

    @zachleat

    Make sure you use `preconnect`! You should check out this talk about the wins you can get with google fonts: zachleat.com/web/five-whys/ The preconnect stuff starts at Slide 50

Shamelessly plug your related post

These are webmentions via the IndieWeb and webmention.io.

Sharing on social media?

This is what will show up when you share this post on Social Media:

How did you do this? I automated my Open Graph images. (Peer behind the curtain at the test page)