CSS @font-face performance optimization

CSS @font-face performance optimization


This article mainly introduces common strategies for font loading optimization. Most of the content is quotation and translation.

1. Basic usage of font-face

The basic usage of font-face must be known to everyone, basically it is similar to this:

@font-face {
	font-family: Lato;
	src: url('font-lato/lato-regular-webfont.woff2') format('woff2'),
		 url('font-lato/lato-regular-webfont.woff') format('woff'),
		 url(font-lato/lato-regular-webfont.ttf) format("opentype");
p { font-family: Lato, serif; }

In this way, we can use custom fonts on our web pages. In addition to the font-family and src attributes, it also has font-style and font-weight attributes. src can specify multiple fonts, which will be applied in order. For example, in the above example, the woff2 font will be loaded first, if it fails, then the woff font will be loaded, otherwise the opentype font will be loaded. The fonts supported by src can have the following types:

The src parameter can be with or without quotation marks, and the meaning of the parameter format is different, such as the following:

src: url(fonts/simple.woff);      /*  simple.woff  */
src: url(/fonts/simple.woff);     /*  simple.woff  */
src: url(fonts/coll.otc#foo);     /*  coll.otc foo  */
src: url(fonts/coll.woff2#foo);   /*  coll.woff2 foo  */
src: url(fonts.svg#simple);       /*  id  'simple' SVG  */

The font address loaded in src is subject to cross-domain constraints. If you want to load fonts across domains, you need to set CORS.

This is the most basic usage of font-face. Next, we will further analyze the usage of font-face and find out optimization strategies as much as possible.

2. When will the font be downloaded?

The basic knowledge of fonts is mentioned above. Have you ever thought about when fonts are downloaded? When we just define the following styles in CSS, will the fonts be automatically downloaded when the page loads?

@font-face {
	font-family: Lato;
	src: url('font-lato/lato-regular-webfont.woff2') format('woff2'),
		 url('font-lato/lato-regular-webfont.woff') format('woff'),
		 url(font-lato/lato-regular-webfont.ttf) format("opentype");

Unfortunately, the font will not be downloaded. Under normal circumstances , only when our page elements use the font defined in font-face, will the corresponding font be downloaded.

Note: Here we are talking about the normal situation. This is because, as long as the font-face is defined in IE8, even if the page element does not use the corresponding font, it will be downloaded.

It s different in other browsers.

For example, in Firefox and IE 9+ , fonts will also be downloaded in the following situations:


<div id="test"></div>


#test {
	font-family: Lato;

What's so special? You may have noticed that although this element uses the font-family: Lato style, it does not have any text! ! ! . According to our ideal situation, it should be that fonts are downloaded only when there is text content. And this is the behavior of Chrome, Safari (WebKit/Blink, etc.) browsers.

Chrome, Safari (WebKit/Blink, etc.) browsers will only download fonts in similar situations as follows:


<div id="test"> </div>


#test {
	font-family: Lato;

So to summarize, the strategies for downloading fonts in different browsers:

  • IE8 will download the font as long as the font-face is defined, regardless of whether the font is actually used.
  • In Firefox, IE 9+, only the font-face is defined and the font is applied to an element on the page, it will be downloaded, regardless of whether the element has text content.
  • Chrome and Safari will download the font only if the font-face is defined, the font is applied to the element on the page, and the element has text content.

Then you may ask, what if our DOM elements are inserted dynamically? such as:

var el = document.createElement('div');
el.style.fontFamily = 'open_sansregular';
el.innerHTML = 'Content.';

The answer is the same, its download strategy is as follows:

var el = document.createElement('div');
el.style.fontFamily = 'open_sansregular';
/*  IE8  */

/*  Firefox, IE 9+   */

el.innerHTML = 'Content.';
/*  Chrome, Safari   */

3. FOIT (Flash of Invisible Text)

FOIT is the default representation of the browser when loading fonts, that is, during the font loading process, the page cannot see the text content. In modern browsers, FOIT can cause this phenomenon to appear for up to 3 seconds. FOIT will lead to a very poor user experience, which we need to avoid as much as possible.

4. FOUT (Flash of Unstyled Text) and font-display attributes

FOUT means to use the default system font during the font loading process. After the font is loaded, the loaded font will be displayed. If the font exceeds FOIT (3s) and has not been loaded yet, the default system font will continue to be used.

IE and Edge will not wait for the FOIT timeout to display the default font, and will immediately display the default font. FOUT is better than FOIT, but you need to pay attention to the reflow it causes.

So in order to make the browser have FOUT behavior, we need to add an attribute to it when setting @font-face: font-display. The default font-display is auto, and the optional attributes and meanings are as follows:

  • auto. The font display policy is user-agent-defined.
  • block. Gives the font face a short block period (3s is recommended in most cases) and an infinite swap period.
  • swap. Gives the font face an extremely small block period (100ms or less is recommended in most cases) and an infinite swap period.
  • 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).
  • optional. Gives the font face an extremely small block period (100ms or less is recommended in most cases) and a 0s swap period.

Generally set to fallback and optional.

5. preload

Add the following code to the page to load fonts faster:

<link rel="preload" href="font.woff2" as="font" type="font/woff2" crossorigin>

Usually used in conjunction with the most basic font usage

Sixth, the font is converted to BASE64URI

This method is to directly change the path when the font is defined in @font-face to the base64 encoding of the font.

Advantages: The advantage of this approach is that it will not produce FOIT and FOUT. So there will be no reflow and repaint. Disadvantages: The font conversion to base64 will also be large, which will affect the first loading speed of the page. It does not support comma-separated format to load fonts in multiple formats, only one format font can be loaded. This leads you to ensure that all browsers are compatible as much as possible, and you usually specify the woff format, because the woff format has good compatibility, but you can't use the smaller woff2 format because the woff2 format is almost compatible.

7. asynchronously load BASE64 format URI fonts

This method is to insert CSS links with BASE64 format URI fonts asynchronously.

8. Use Font Load API + FOUT + class to switch

In this way, the class that uses @font-face is not used at the beginning of the period, and then use the Font Load API to load the font we want to use, and then switch the corresponding CSS. The Font Load API is a native API:

document.fonts.load('1em open_sansregular')
.then(function() {
	var docEl = document.documentElement;
	docEl.className += ' open-sans-loaded';

.open-sans-loaded h1 {
	font-family: open_sansregular;

Of course, this method needs to consider the issue of browser compatibility.

9. FOFT (Flash of Faux Text)

FOFT will divide the font loading into multiple parts, first load the Roman web font, and then immediately use the font-synthesis property to render the bold and italic variants when the real bold and italic are loaded.

This method is based on the [ Font Load API + FOUT + class ] method, which is very suitable for loading scenes with the same font but different weights and glyphs, such as Roman, bold, italic, bold italic, etc. We divide these fonts into 2 stages: the first stage is Roman fonts, then the artificial bold and italics are rendered immediately, and finally (the second stage) is replaced with real fonts. It is also possible to use sessionStorage to optimize access to repeated view scenarios.


The only difference between CRITICAL FOFT and standard FOFI is the loading of Roman fonts in the first stage. CRITICAL FOFT will not load the complete set of Roman fonts, but only a subset of it (such as A-Za-z0-9). Load in the second stage.


The only difference between this and CRITICAL FOFT is the way that Roman subset fonts are loaded. The previous is done with Font Load API, here will be hard-coded Ma subset fonts into the form of BASE64 URI to load.


The only difference between this and the above is the way of loading the Roman subset fonts in the first stage, which is loaded in the form of preload.


The overall font loading strategy can be summarized as follows with this figure:


This article is mainly translated from the following blog post

  1. https://www.zachleat.com/web/comprehensive-webfonts/#font-display
  2. https://dev.opera.com/articles/better-font-face/

"IVWEB Technology Weekly" is shockingly launched. Pay attention to the official account: IVWEB community, and push high-quality articles regularly every week.

  • Weekly article collection: weekly
  • Team open source project: Feflow