1. Introduction
When using downloadable webfonts via @font-face, the user agent needs to know what to do while the font is actively loading. Most web browsers have adopted some form of timeout:
Browser | Timeout | Fallback | Swap |
---|---|---|---|
Chrome 35+ | 3 seconds | yes | yes |
Opera | 3 seconds | yes | yes |
Firefox | 3 seconds | yes | yes |
Internet Explorer | 0 seconds | yes | yes |
Safari | 3 seconds | yes | yes |
-
Chrome and Firefox have a 3 second timeout after which the text is shown with the fallback font. Eventually, a swap occurs: the text is re-rendered with the intended font once it becomes available.
-
Internet Explorer has a 0 second timeout which results in immediate text rendering: if the requested font is not yet available, fallback is used, and text is rerendered later once the requested font becomes available.
While these default behaviors are reasonable, they’re unfortunately inconsistent across browsers. Worse, no single approach is sufficient to cover the range of use-cases required by modern user-experience– and performance–conscious applications.
The Font Loading API [CSS-FONT-LOADING-3] allows a developer to override some of the above behaviors, but that requires scripting, a non-trivial amount of effort, and ultimately doesn’t provide sufficient hooks to cover all reasonable cases. Additionally, the developer needs to either inline the loading script into their page or load an external library, introducing additional network latency before the fonts can be loaded and delaying text rendering.
Design/performance-conscious web developers have a good sense for the relative importance of a given web font for the intended user experience. This specification provides them the ability to control font timeout and rendering behavior. Specifically, it lets developers:
-
Define the font display policy when text is ready to be painted: block, or paint with fallback.
-
Define the font display policy once the desired font is available: rerender text with the new font, or leave it with the fallback.
-
Define custom timeout values for each font.
-
Define custom display and timeout policies per element.
2. The Font Display Timeline
At the moment the user agent first attempts to use a given downloaded font face on a page,
the font face’s font download timer is started.
This timer advances through three periods of time associated with the font face—
-
The first period is the font block period. During this period, if the font face is not loaded, any element attempting to use it must instead render with an invisible fallback font face. If the font face successfully loads during the block period, the font face is then used normally.
-
The second period, occuring immediately after the block period, is the font swap period. During this period, if the font face is not loaded, any element attempting to use it must instead render with a fallback font face. If the font face successfully loads during the swap period, the font face is then used normally.
-
The third period, occuring immediately after the swap period, is the font failure period. If the font face is not yet loaded when this period starts, it’s marked as a failed load, causing normal font fallback. Otherwise, the font face is used normally.
To render with a fallback font face for a given element, the user agent must find the first font face specified in the element’s font-family list which is already loaded, and use that for rendering text. Doing this must not trigger loads of any of the fallback fonts.
To render with an invisible fallback font face for a given element, find a font face as per "render with a fallback font face". Create an anonymous font face with the same metrics as the selected font face but with all glyphs "invisible" (containing no "ink"), and use that for rendering text. Doing this must not trigger loads of any of the fallback fonts.
fallback and optional can result in some faces in a family being used while others are required to fallback, giving a "ransom note" look. Perhaps require that all fonts in a family have the same behavior (all swapped in, or all fallback)? See also the @font-feature-values for controlling the behavior on a font family basis.
3. Controlling Font Display Per Font-Face: the font-display descriptor
The font-display descriptor for @font-face determines how a font face is displayed, based on whether and when it is downloaded and ready to use.
Name: | font-display |
---|---|
For: | @font-face |
Value: | auto | block | swap | fallback | optional |
Initial: | auto |
Note: For all of these values, user agents may use slightly different durations, or more sophisticated behaviors that can’t be directly expressed in the font-display syntax, in order to provide more useful behavior for their users. They may also provide the ability for users to override author-chosen behavior with something more desirable; for example, forcing all fonts to have a 0s block period.
- auto
-
The font display policy is user-agent-defined.
Note: Many browsers have a default policy similar to that specified by block.
- block
-
Gives the font face a short block period (3s is recommended in most cases)
and an infinite swap period.
Note: In other words, the browser draws "invisible" text at first if it’s not loaded, but swaps the font face in as soon as it loads.
This value must only be used when rendering text in a particular font is required for the page to be usable. It must only be used for small pieces of text.
For example, badly designed "icon fonts" might associate a "⎙" (print) icon with an unrelated character like "C", so if the text is displayed with a fallback font instead there will be confusing letters scattered around the page rather than the desired icon. In this case, temporary blank spots are better than using a fallback font.(However, the fallback font is used eventually, as having confusing letters scattered around the page is better than having links and such never show up at all.)
- swap
-
Gives the font face a 0s block period and an infinite swap period.
Note: In other words, the browser draws the text immediately with a fallback if the font face isn’t loaded, but swaps the font face in as soon as it loads.
This value should only be used when rendering text in a particular font is very important for the page, but rendering in any font will still get a correct message across. It should only be used for small pieces of text.
- fallback
-
Gives the font face an extremely small block period (100ms or less is recommended in most cases)
and a short swap period (3s is recommended in most cases).
Note: In other words, the font face is rendered with a fallback at first if it’s not loaded, but it’s swapped in as soon as it loads. However, if too much time passes, the fallback will be used for the rest of the page’s lifetime instead.
This value should be used for body text, or any other text where the use of the chosen font is useful and desired, but it’s acceptable for the user to see the text in a fallback font. This value is appropriate to use for large pieces of text.
For example, in large pieces of body text, it’s most important just to get the text rendered quickly, so the user can begin to read as quickly as possible. Further, once the user has started reading, they shouldn’t be disturbed by the text suddenly "shifting" as a new font is swapped in, as that’s distracting and annoying to re-find where one was in the text. - optional
-
Gives the font face an extremely small block period (100ms or less is recommended in most cases)
and a 0s swap period.
If the font is not retrieved before the two durations expire, the user agent may choose to abort the font download, or download it with a very low priority. If the user agent believes it would be useful for the user, it may avoid even starting the font download, and proceed immediately to using a fallback font.
Note: In other words, the font is used if it’s already downloaded and available, but otherwise a fallback is used for the rest of the page’s lifetime instead. The font might download in the background and be available to future page loads, but if the user-agent detects that the user has very limited bandwidth, it might choose to simply never download and use the font.
This value should be used for body text, or any other text where the chosen font is purely a decorative "nice-to-have". It should be used anytime it is more important that the web page render quickly on first visit, than it is that the user wait a longer time to see everything perfect immediately.
For example, body text is perfectly readable in one of the browser default fonts, though a downloadable font face may be more attractive and mesh with the site’s aesthetics better. First time visitors to a site generally care far more about the site being quickly usable than they do about the finer points of its display, and optional provides a good behavior for them. If they return later, the desired font faces might have finished downloading, giving them the "intended" experience without slowing down either their first or subsequent visits.Users on very slow connections might not ever receive the "intended" experience, but optional ensures they can actually use the site, rather than quitting and going elsewhere because the site takes too long to load.
4. Controlling Font Display Per Font-Family via @font-feature-values
The font-display descriptor for @font-feature-values determines how a font family is displayed, by setting the "default" font-display value for @font-face rules targeting the same font family. When font-display is omitted in an @font-face rule, the user agent uses the font-display value set via the @font-feature-values/font-display for the relevant font-family if one is set, and otherwise defaults to "font-display: auto".This mechanism can be used to set a default display policy for an entire font-family, and enables developers to set a display policy for @font-face rules that are not directly under their control. For example, when a font is served by a third-party font foundry, the developer does not control the @font-face rules but is still able to set a default font-display policy for the provided font-family. The ability to set a default policy for an entire font-family is also useful to avoid the ransom note effect (i.e. mismatched font faces) because the display policy is then applied to the entire font family.
Name: | font-display |
---|---|
For: | @font-feature-values |
Value: | auto | block | swap | fallback | optional |
Initial: | auto |
-
required / important / preferable / optional
5. Acknowledgements
Special thanks to Ilya Grigorik and David Kuettel for their help in developing this specification.