One of the downsides of using web fonts is that if a font is not available on a user’s device, it must be downloaded. This means that before the font becomes available the browser has to decide how to handle the display of any block of text that uses that font. And it needs to do so in a way that doesn’t significantly impact the user experience and perceived performance.

In the course of time, browsers have adopted several strategies to mitigate this problem. But they do this in different ways and out of the control of developers, who have had to devise several techniques and workarounds to overcome these issues.

Enter the font-display descriptor for the @font-face at-rule. This CSS feature introduces a way to standardize these behaviors and provide more control to developers.

How to use font-display

Before looking in detail at the various features offered by font-display, let’s briefly consider how you might use the feature in your CSS.

First of all, font-display is not a CSS property but, as mentioned in the intro, it is a descriptor for the @font-face at-rule. This means that it must be used inside a @font-face rule, as showed in the following code:

@font-face {
font-family: 'Roboto Condensed';
src: url(fonts/robotocondensed.woff2) format('woff2');
font-display: swap;
}

In this snippet I’m defining a swap value for the font Roboto Condensed.

The keywords for all the available values are:

  • auto
  • block
  • swap
  • fallback
  • optional

The initial value for font-display is auto.

Let’s look at how each of these keywords manages the font loading and display process.

font-display: auto

This value tells the browser to adopt the default font display behavior chosen by the browser. Often this strategy is similar to the next value, block.

font-display: block

With this value, after a short block period (the specification recommends a duration of three seconds), the swap period extends to infinity. This means that in this circumstance the failure period is absent.

While the browser briefly waits for the requested font, it renders the text with the invisible fallback font; after that, if the font is not yet available, the fallback font is made visible; and whenever the download completes, the browser re-renders the text with the wanted font.

At the beginning of page load, the heading is invisible but it is there, in the DOM. After about three seconds, if the font is not yet available, the text is made visible with the fallback font. In the video demo, I’m mimicking poor network conditions using Chrome DevTools’ network throttling feature. Finally, when the font manages to download, the heading is re-rendered with it.

font-display: swap

With this value, the block period collapses to 0 and the swap period extends to infinity. Therefore, here too, the failure period is missing.

In other words, the browser doesn’t wait for the font but instead immediately renders the text with the fallback font; then, whenever the font is available, the text is re-rendered with it.

font-display: fallback

This is the first value that incorporates the failure period. After a very short block period (100 ms is recommended), the swap period now has a short duration (3s is recommended). As a result, if the requested font is not ready at the end of this period, the text will display using the fallback font for the duration of the page visit. This avoids disturbing the page visitor with a late layout shift that could be jarring to the user experience.

font-display: optional

When I first read the specification, I found the names assigned to the font display strategies not so clear. This is even pointed out in the specification itself, which suggests future versions of the spec use names that better illustrate the intended use of each strategy, proposing the following alternatives:

  • requires for block
  • important for swap
  • preferable for fallback

But the optional value is expected to remain unchanged. Indeed this value nicely captures the essence of the behavior it triggers. In this case, the font is considered optional for the rendering of the page, essentially telling the browser: if the font is already available, use it, otherwise it doesn’t matter, go ahead with the fallback font; the font can be ready for use on future page visits.

With this value, the font display timeline has a short block period (again, the spec recommends a 100 ms time interval) and a zero-duration swap period. Hence the failure period immediately follows the block period, meaning that if the font is not readily available, it will not be used for the duration of the page visit. But the font could eventually be fully downloaded in the background and so it would become available for immediate rendering on future page loads.

But I should point out here that, especially under poor network conditions, the user agent is free to abort or even to not begin the font download. This is so as to not unnecessarily impact the quality of the network connection. Therefore the site is still usable but the font won’t be immediately available on future page loads.

Before moving on, note the extremely short duration of around 100 ms recommended for the block period when using the fallback and optional values. This is to allow a brief period for a quick-loading font (or one loading from the cache) to display before using the fallback font, thus avoiding a “flash of unstyled text”, or FOUT.

I actually wondered why the block period collapses to zero when using font-display: swap, instead of using the same short interval as optional. It turns out, there is an open issue in the spec’s GitHub repo to make ‘swap’ use the same “tiny block period” as others.

About the Fallback Font

In the above discussion, several times I mentioned the fallback font. But where does this come from?

The fallback font is the first available font present in the font stack defined using the font-family property on the element in question.

For example, on the test page, the font-family value for the heading is:

h1 {
  font-family: 'Roboto Condensed', Arial, "Helvetica Neue", Helvetica, sans-serif;
}

This can be verified (see the video above for optional), for example, on a Windows machine, which uses Arial as the rendered font.

Support

At the time of writing support for the font-display descriptor looks as follows:

  • Chrome has supported it since version 60
  • Opera has supported it since version 47
  • It’s in development for Firefox and has been available behind a flag since version 46.
  • Regarding Safari, the WebKit platform status reports that it is in development
  • There is no indication yet that Microsoft Edge will support it anytime soon. There is a ticket on the Microsoft Edge Developer Feedback site where it is possible to vote for the implementation of this feature.

Please refer to caniuse.com for up-to-date support information.