zachleat’s Ugly Mug

Zach Leatherman

23 Minutes of Work for Better Font Loading

21 November 2017 Read in about 4 minutes

Last night I hopped on Twitter after my daughter went to sleep with a little bit of time to kill before a 9 PM NEJS CONF 2018 planning meeting. What greeted me was perhaps the most tailored, best targetted content I’ve seen on the platform since Donald’s Twitter account was deactivated for a blissful 11 minutes.

8:29 PM, 31 minutes to meeting

I can do that! I can do that! Move move move move, I can do that I can do that!

8:33 PM, 27 minutes to meeting

To confirm that code changes I made would be helpful, I asked:

She replied quickly with a confirmation.

8:37 PM, 23 minutes to meeting

Work was underway. I have not worked on this project before. I didn’t know anything about the project’s structure or build process—but as luck would have it, they were all tools I’d used before. I forked the repo. I pulled the code down using git. I set up a virtual host for localhost previews. It was all happening. I did a project search for @font-face and found two: 8bit Art Sans and VT323.

Let’s make some changes

  1. Delete all of the web font files that aren’t TTF files from the project—we’re gonna make our own.
  2. Use glyphhanger to subset the fonts automatically to the code points used on the actual site (while also including ASCII). I ran:
    • glyphhanger http://make8bitart.localhost/ --US_ASCII --subset=assets/fonts/*.ttf
    • Note that glyphhanger outputs optimized subset WOFF2, WOFF (with zopfli encoding for more savings), and TTF files.
  3. Update the @font-face CSS blocks to point to the new subset files and remove all of the other formats—we’re only using WOFF2, WOFF, and TTF here. Maybe glyphhanger needs a feature to help with this step too!
  4. Add font-display: swap for full FOUT on supporting browsers (just Chrome right now but support will grow over time).
  5. preload both web fonts to make requests start earlier and reduce FOIT and FOUT:
    • <link rel="preload" href="FILE_PATH.woff2" as="font" type="font/woff2" crossorigin>
  6. Update the Service Worker to cache the WOFF2 versions only, since Service Worker is a browser support subset of WOFF2.
  7. Check it all in and make a pull request
glyphhanger command output, automatically subsetting both web fonts.

9:01 PM, -1 minute to meeting

Whew. Well—okay, so I was a little late to the meeting but the pull request was opened at 9:00 PM on the dot so I’m counting it.

Performance

(Fast 3G network throttled, Chrome)

Let’s see how the page loads before and after we made changes.

Before

  • First paint: 773ms (screenshots do not include network time)
  • Page weight: 296 KB (dang, Jenn—nice work)
  • Font weight (TTF and WOFF): 7.1 KB + 84.8KB = 91.1 KB
  • FOIT:
    • 773ms -> 1.91s for 8bit Art Sans: 1.137s total
    • 773ms -> 3.72s for VT323: 2.947s total (just under that 3s FOIT timeout window)
  • FOUT: none

After

  • First paint: 763ms (similar)
  • Page weight: 215 KB
  • Font weight (WOFF2): 4.2 KB + 6.5 KB = 10.8 KB (11% of original, savings of 80.3KB)
  • FOIT: none, font-display would have kicked in if preload hadn’t beat it to the punch.
  • FOUT:
    • none for 8bit Art Sans, preload is killing it.
    • none for VT323, preload is killing it.

Recap

Font loading can be daunting and even a little confusing at times. If you’re feeling overwhelmed by the topic, the above steps can serve as a shortlist of things you can quickly do to make an improvement to your site. It doesn’t have to be the best—just make it better!

Jenn really has a nice site here. It was simple, easy to work on, and to be fair—fast already. But for web fonts, there were a few changes that helped. We saved 80KB of web font content by subsetting. We’ve eliminated FOUT and FOIT on a Fast 3G connection using preload. If on a slower connection, we’re using font-display in browsers that support it to FOUT instead of FOIT.

Not bad for 23 minutes worth of work.