Improving web font performance: a case study
Tags: webperf, webfont
Published: 2019-04-02 06:09:35

---
title: Improving web font performance: a case study
published: true
description: Fonts can have a great impact on web performance. A thorough analysis to optimize it can be well worth your efforts. Read the case study and learn why. 
tags: webperf, webfont
canonical_url: https://www.netcentric.biz/insights/2019/01/web-font-performance.html
---

On the 8th and 9th of November 2018, I had the chance to join the first edition of the [performance.now() conference](https://perfnow.nl/), focusing on today's most important web performance insights. Among the [sixteen great talks](https://perfnow.nl/speakers), I took a great deal away from Zach Leatherman's presentation on Web Font Performance and I would encourage you to watch [the full talk](https://www.youtube.com/watch?v=FbguhX3n3Uc).

At Netcentric, where I am a Front-end Software Engineer and web performance guardian, I could immediately see areas in which I could begin to try out some of the tips mentioned to improve web font implementation. I'll illustrate a few points of analysis that could be useful for optimizing web font performance.

# Insights for optimizing web font performance

**Setup**

I mainly used macOS 10 with Chrome 70 for my analysis, and always throttling the network to Fast 3G and CPU 4x slowdown.

## Similar fonts

Let's start by simply loading the homepage of the website to see which files are being loaded:

![Network tab](https://thepracticaldev.s3.amazonaws.com/i/abeotpfrvfiwr537fsu9.png)

We are loading 5 font files, the .ttf and the CAC-Regular cannot be touched as they are very specific but I noticed that we have 3 fonts with quite a similar name:

- CS-Light.woff2
- CS-Regular.woff2
- CS-Demi.woff2

I wondered how different they really are and if we really needed all of them, so I decided to compare them visually by placing one font on top of another. I chose to use the CS-Light font as a base font because it seemed easier to match with the other fonts. In our case, setting a lighter font-weight to the CS-Regular font didn't make any difference, so I could only apply a bolder font-weight.

So, I have the CS-Regular font in green, the one I want to replace, and the CS-Light in red and they don't really match.

![We obviously have 2 different fonts](https://thepracticaldev.s3.amazonaws.com/i/ogd4t59etq43b4c4gdrg.png)
<figcaption>We obviously have 2 different fonts</figcaption>

The first word here looked acceptable, but then it gets pretty wild.  However, I could see some similarities so I tried to get a closer match by playing a little with some CSS:

{% gist https://gist.github.com/armelpingault/4e60f9a881fca5bdfec0822cf914d2a4 %}
<figcaption>Adding a letter-spacing and a line-height to .cs-light</figcaption>

By playing with two CSS properties, `letter-spacing` and `line-height`, which are both well supported, I could actually get a better match:

![Fonts overlay](https://thepracticaldev.s3.amazonaws.com/i/jfasb6vyk6zzgbj94sd4.png)

This isn't perfect, but I was surprised at how close I could get and how similar those two fonts are.

I also wanted to make sure they still match even if I change the `font-size`:

![Font-size: 1em](https://thepracticaldev.s3.amazonaws.com/i/hg4zovj56hhbxidvw5zh.png)
<figcaption>Font-size: 1em</figcaption>

![Font-size: 3em](https://thepracticaldev.s3.amazonaws.com/i/wm8jnfc6tjbntcux1yl2.png)
<figcaption>Font-size: 3em</figcaption>

I really liked the result. You can feel a slight difference, especially when the text is made a bit larger, but overall it looks similar.

Then, I wanted to also check the other font we have, called CS-Demi, to see if I could get similar results:

![CS-Light cs CS-Demi](https://thepracticaldev.s3.amazonaws.com/i/8w3is0pdqqe08pprmii4.png)<figcaption>CS-Light cs CS-Demi</figcaption>

Again, it didn't really match in the first place. So, I went through the same process and played again with some CSS properties, also adding a `font-weight` property since this font was much bolder than the other one:

{% gist https://gist.github.com/armelpingault/9975cd34de40ec55d57dbfd37603e55b %}
<figcaption>Also adding a font-weight property</figcaption>

In the end, I got a pretty decent result. It's not as good as the previous results, but it's still hard to tell the difference between these two fonts if you don't know exactly which is which:

![Font-size: 2em](https://thepracticaldev.s3.amazonaws.com/i/xmvrlf55139p9ypvn26w.png)<figcaption>Font-size: 2em</figcaption>

So, now we can get a pretty similar result by loading only one file instead of three. Let's see what the gain could be:

![344.7 KB of web fonts](https://thepracticaldev.s3.amazonaws.com/i/h0he3ta47jp4cp9bfgwi.png)<figcaption>344.7 KB of web fonts</figcaption>

We are loading **344.7 KB** of web fonts separated in 5 file requests.

After getting rid of CS-Regular and CS-Demi, we now have only **197.7 KB** of web fonts among 3 file requests, so that's a gain of **147.KB**, which represents **~42.6%** of the total weight of the fonts of the page. Not bad.

## FOIT and FOUT

Another problem we've faced with websites is called Flash Of Invisible Text (FOIT) and also Flash Of Unstyled Text (FOUT). You can definitely see it visually when the network is a bit slower than usual. The Lighthouse audit has also been complaining about it:

![The loading of web fonts are timing out (3,000ms)](https://thepracticaldev.s3.amazonaws.com/i/0amfaav40ms2e0tsdyp8.png)<figcaption>The loading of web fonts are timing out (3,000ms)</figcaption>

Thanks to the [font-display](https://developer.mozilla.org/en-US/docs/Web/CSS/@font-face/font-display) property, we can avoid a FOIT pretty easily, using the fallback value:

{% gist https://gist.github.com/armelpingault/d87a33f50dfdca32b29aa32bfb30aa6d %}
<figcaption>Font-display: fallback</figcaption>

As explained on the [Google Developers' website](https://developers.google.com/web/updates/2016/02/font-display): 

fallback gives the font face an extremely small block period (100ms or less is recommended in most cases) and a short swap period (three seconds is recommended in most cases). In other words, the font face is rendered with a fallback at first if it's not loaded, but the font is swapped as soon as it loads. However, if too much time passes, the fallback will be used for the rest of the page's lifetime. fallback is a good candidate for things like body text where you'd like the user to start reading as soon as possible and don't want to disturb their experience by shifting text around as a new font loads in.

Be careful though: `font-display` is [not fully supported](https://caniuse.com/#search=font-display) yet!

Finally, after adding this property, we can fix our FOIT, at least on browsers supporting this feature:

![FOIT is not a problem anymore](https://thepracticaldev.s3.amazonaws.com/i/6oybby18qtbix9kh5evv.png)<figcaption>FOIT is not a problem anymore</figcaption>

However, our FOUT problem remained, so to fix this issue I took a look at the preload hint.

## Preload

![We need to prioritize our CS-Light request](https://thepracticaldev.s3.amazonaws.com/i/lc7vc0q74pjk8pzmzeyr.png)<figcaption>We need to prioritize our CS-Light request</figcaption>

By examining the timing of the request of our CS-Light web font, I could see that it took almost 5 seconds to load, and it's also being loaded at the same time as the other fonts:

![Before preload](https://thepracticaldev.s3.amazonaws.com/i/pdclv8miholu16vi5n37.png)<figcaption>Before preload</figcaption>

So I decided to preload the critical web font by simply adding 1 line on the `head` section (see [preload specifications](https://w3c.github.io/preload/)):

{% gist https://gist.github.com/armelpingault/99023e7eaf03906369d25b35e6b62702 %}
<figcaption>Preload the main font file</figcaption>

I could then see straight away that Chrome was loading the main font file before the other ones, and faster too:

![Our critical font file has now a high priority](https://thepracticaldev.s3.amazonaws.com/i/10pmol12t5zfa7bx1zyz.png)<figcaption>Our critical font file has now a high priority</figcaption>

![Time to download the resource is divided by 2](https://thepracticaldev.s3.amazonaws.com/i/m4s0vf32391lltjrfooh.png)<figcaption>Time to download the resource is divided by 2</figcaption>

Therefore, we started to download our critical web font resource after **1.45s** instead of **5.33s**, resulting in a gain of **72.8%**. This meant it took **2.14s** instead of **4.29s** to download, meaning a gain of **50%** regarding the length of time taken to download the content.

After all those optimizations, we could finally put everything together. We ended up with something that looked like this:

{% gist https://gist.github.com/armelpingault/33c836c5bbc713f9a15f7e300b59c0ac %}

## Next steps

There is still room for improvement regarding the optimization of the web fonts, and I'll continue to share the rest of the analysis. The next area to explore will be the possibility of subsetting fonts, since the website is being delivered in many languages.
 
Feel free to share your experience regarding web fonts optimization below or get in touch if you have any questions about these insights.