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.
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:
<span><span>@font-face</span> { </span> <span>font-family: 'Saira Condensed'; </span> <span>src: <span>url(fonts/sairacondensed.woff2)</span> format('woff2'); </span> <span>font-display: swap; </span><span>}</span>
In this snippet I’m defining a swap value for the font Saira Condensed.
The keywords for all the available values are:
The initial value for font-display is auto.
In later sections I’ll go over each of these values in detail. But first, let’s look at the time period that the browser uses to determine the font to be rendered. When discussing each of the values, I’ll explain the different aspects of the timeline and how these behave for each value.
At the core of this feature is the concept of the font-display timeline. The font loading time, starting from its request and ending with its successful loading or failure, can be divided into three consecutive periods that dictate how a browser should render the text. These three periods are as follows:
Adjusting the duration of such periods allows you to configure a custom text rendering strategy. In particular, these durations can collapse to zero or extend to infinity, as I’ll show you in the following sections.
But these durations cannot be explicitly assigned by the developer. This possibility was examined in an early stage of the specification, but was dropped. Instead, a set of predefined keyword values that can handle the majority of use cases are provided, as outlined in the previous section.
Let’s look at how each of these keywords manages the font loading and display process.
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.
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.
You can watch this behavior in the following video, which uses a simple test page that incorporates a specific web font for its heading:
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.
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.
Let’s verify this:
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.
In this first video below, the font loads after more than six seconds, thus it is never swapped in:
In the next video, the font loads faster, before the timeout of the swap period kicks in, so the font is used as expected:
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:
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.
In the video below, the test page is loaded without throttling the network. The font is downloaded quickly, but only after the short block period, so the text is displayed with the fallback font for all duration of the visit.
In the next video, the page is reloaded under the same network conditions, but this time with the cache enabled, to simulate a second visit:
And there you have it, the heading now renders with the desired web font.
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.
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:
<span><span>@font-face</span> { </span> <span>font-family: 'Saira Condensed'; </span> <span>src: <span>url(fonts/sairacondensed.woff2)</span> format('woff2'); </span> <span>font-display: swap; </span><span>}</span>
This can be verified (see the video above for optional), for example, on a Windows machine, which uses Arial as the rendered font.
At the time of writing support for the font-display descriptor looks as follows:
Please refer to caniuse.com for up-to-date support information.
It is worth noting that font-display support cannot be tested by feature queries, because, as mentioned above, it is not a CSS property but a font descriptor. In this GitHub issue you’ll find some discussion on how to properly detect this feature.
Once it has been detected that font-display is not supported, several fallback strategies are possible, but this is out the scope of this article. The article A Comprehensive Guide to Font Loading Strategies by Zach Leatherman presents an exhaustive survey of available solutions.
You may have noticed that the font used in the demo page is from Google Fonts, but it is not loaded in the usual way, i.e., linking to the stylesheet provided by the font provider. Instead, I just copied the URL of the font found in that stylesheet and used that URL in my custom @font-face rule. I had to do this because, as seen in the usage section, font-display must be specified inside the font-face rule.
Is there a better and more Google Fonts-friendly way? Are Google Fonts and other third-party font foundries going to support font-display?
There is an open issue on the Google Fonts GitHub repo where this is discussed. Add your 1 to show your interest in this feature!
Also, it’s worth mentioning that the CSS Fonts Module Level 4 proposes the usage of font-display as a descriptor for @font-feature-values, to enable developers to set a display policy for @font-face rules that are not directly under their control. But this is not yet implemented by any user agent.
I hope this gives you a decent overview of the font-display descriptor and how this feature foreshadows a strong future for font rendering on the web.
Although this article didn’t discuss specific use cases for the different strategies implemented by font-display, the specification illustrates use cases with some clear examples, and several of the cited references elaborate on this topic. So in addition to the basics I’ve covered here, you’ll have more to look over in the resources I’ve referenced.
The CSS font-display property is a CSS feature that controls how fonts are displayed on a webpage, particularly during the period when the font is still loading. This property is crucial because it significantly impacts the user experience on a website. If a font takes too long to load, it can cause a delay in the rendering of the text, leading to a phenomenon known as Flash of Invisible Text (FOIT) or Flash of Unstyled Text (FOUT). By using the font-display property, developers can control this behavior and improve the overall user experience.
The CSS font-display property works by providing a set of rules that dictate how a font should behave while it is loading and when it fails to load. It accepts several values, including ‘auto’, ‘block’, ‘swap’, ‘fallback’, and ‘optional’. Each of these values represents a different loading strategy, giving developers the flexibility to choose the one that best suits their needs.
Each font-display value represents a different font loading strategy. ‘Auto’ leaves the loading behavior up to the browser. ‘Block’ gives the font a short block period and an infinite swap period. ‘Swap’ gives the font a zero-second block period and an infinite swap period. ‘Fallback’ gives the font a very short block period and a short swap period. ‘Optional’ gives the font a zero-second block period and a zero-second swap period.
To use the CSS font-display property in your code, you need to include it in your @font-face rule. Here’s an example:
@font-face {
font-family: 'MyFont';
src: url('myfont.woff2') format('woff2');
font-display: swap;
}
In this example, the font-display property is set to ‘swap’, which means the text will be displayed immediately with a fallback font if ‘MyFont’ is not available.
The CSS font-display property can significantly impact website performance. By controlling how fonts are displayed during the loading process, it can help prevent delays in text rendering, thereby improving the perceived loading speed of a website. It can also help avoid layout shifts caused by late-loading fonts, leading to a smoother user experience.
The CSS font-display property directly addresses the issues of FOIT and FOUT. By allowing developers to control the font loading behavior, it can prevent the text from becoming invisible or unstyled during the loading process. For example, setting the font-display property to ‘swap’ can eliminate FOIT by displaying the text immediately with a fallback font.
Some best practices for using the CSS font-display property include choosing the right value based on your needs and testing the loading behavior on different browsers and network conditions. It’s also recommended to use a fallback font that closely matches the metrics of the custom font to minimize layout shifts.
Yes, you can use the CSS font-display property with web fonts hosted on third-party services. However, it’s important to note that not all services support this feature. You should check the documentation or contact the service provider for more information.
One potential drawback of using the CSS font-display property is that it’s not fully supported in all browsers. For example, Internet Explorer and some older versions of other browsers do not support this feature. Additionally, the ‘optional’ value may cause the custom font to be skipped entirely in some cases, which could affect the visual consistency of your website.
The CSS font-display property is just one tool in a comprehensive web performance strategy. While it can help improve the perceived loading speed and prevent layout shifts, it should be used in conjunction with other performance optimization techniques, such as minimizing the size of your CSS and JavaScript files, optimizing images, and using a content delivery network (CDN).
The above is the detailed content of CSS font-display: The Future of Font Rendering on the Web. For more information, please follow other related articles on the PHP Chinese website!