Font-display

This is a small explainer that I built for a talk on web fonts and performance.

Before we talk about what font-display is, let's talk about the lifetime of a web font. For a super detailed explanation of where web fonts fit in the browser rendering process, Ilya Grigorik has an amazing blog post on web font optimization. But, if we're just trying to understand the basics, there are basically three parts to it:

During the block period, the browsers renders your text in an invisible font. This is why on a lot of web font heavy website, during the first load of the page you will see no text or worse, phantom underlines.

During the swap period, the browser renders your text in the fallback font (in the example in the diagram, that would be the default `serif` font.

The failure period means no font has been found, in which case the browser renders your text in the fallback font, as above.


With the new font-display attribute, the browser can control the length of each of these periods, and what happens when one of them fails. There are 4 different values: block, swap, fallback and optional. There's also auto, which usually ends up being the same as block.

The font display is controlled per font-face:

      
      @font-face {
          font-family: 'my-font';
          font-display: optional;
          src: url(my-font.woff2) format('woff2');
      }
      

font-display: block

The text blocks (is invisible) for a short period*. Then, if the custom font hasn't been downloaded yet, the browser swaps (renders the text in the fallback font), for however long it takes the custom font to be downloaded, and then re-renders the text in the custom font. You get a FOIT (flash of invisible text).

*The length of the block period depends on which browser you're using. Chrome, Firefox and (recently) Safari use 3s. IE uses 0s (effectively having no block period), and older Safari used no timeout, effectively having an inifinite block period.

Supported: everywhere;

font-display: swap

There is no block period (so no invisible text), and the text is shown immediately in the fallback font until the custom font loads, then it's swapped with the custom font. You get a FOUT (flash of unstyled text).

Supported: Chrome 60, Firefox behind a flag (layout.css.font-display.enabled).

font-display: fallback

This is somewhere in between block and swap. The text is invisible for a short period of time (100ms). Then if the custom font hasn't downloaded, the text is shown in a fallback font (for about 3s), then swapped after the custom font loads.

Supported: Chrome 60, Firefox behind a flag.

font-display: optional

This behaves just like fallback, only the browser can decide to not use the custom font at all, based on the user's connection speed (if you're on a slow 3G or less, it will take forever to download the custom font and then swapping to it will be too late and extremely annoying)

Supported: Chrome 60, Firefox behind a flag.

Side-by-side, that looks like this:

Demo

This demo simulates a slow connection speed. The font resource is returned after a 4s delay (so after the block period would normally end), so that you can see what happens for each of the options.

Important note: once you run the demo and the browser has downdloaded your custom font, it will save it in the font cache, which is why if you refresh the page and run the demo again, you'll see the custom font appear instantly. You'll have to do a hard refresh (Cmd+Shift+R in Chrome) to reload the page without the cache.

0.0
s

Very slow font (4s server delay)

Medium slow font (2s server delay)

Fast font (0s server delay)

So, what should I do?

This depends a lot on how you are using your webfont, and whether rendering the text in a fallback font makes sense. For example, if you're rendering the main body text on a site, you should use font-display:optional. On browsers that implement it, like Chrome, the experience will be much nicer: your users will get fast content, and if the web font download takes too long, they won't get a page relayout halfway through reading your article.

If you're using a web font for icons, there is no acceptable fallback font you can render these icons in (unless you're using emoji or something), so your only option is to completely block until the font is ready, with font-display:block.




Happy fonting! ❤️, monica