Share on Twitter
Share on Facebook
Share on HackerNews
Share on LinkedIn

Web Fonts and the Dreaded Cumulative Layout Shift

How frustrating is it when you’ve just landed on a web page, you click on a certain element and an ad or something else pops up and you end up clicking that thing instead? That’s a layout shift, which is bad for the user’s experience and the later they happen, the worse it is.

Research from HTTP Archive shows that over 80% of websites use web fonts. Web fonts also cause layout shifts, if they’re not being loaded strategically. This means your page probably has cumulative layout shifts (CLS). In this article we’ll explore different loading strategies to improve your page’s performance and minimize cumulative layout shifts when using web fonts.

How CLS is calculated?

The formula behind calculating CLS is pretty simple. It is the multiplication of the impact fraction with the viewport distance fraction.

CLS = impact fraction * viewport distance fraction

The browsers obtain the values by measuring how the “unstable elements” move between two animation frames. The impact fraction is a decimal number between 0 and 1, representing 0% and 100% of the changes relative to the viewport. The viewport distance fraction is the distance the element moved between the two animation frames, which just like the impact fraction is a decimal number between 0 and 1.

What’s considered a good CLS score?

CLS Score Bar For the best user experience, every page should aim to have a CLS score lower than 0.1. Also, not every device will give you the same CLS score. Outside factors like device performance and internet speed affect the CLS score, so the one you see in your dev tools in Chrome won’t necessarily be the one your users will experience. To keep an eye on what the actual CLS score is, you need to implement monitoring tools in your app. With an application performance monitoring tool like Sentry, you’ll have access to other performance metrics, such as web vitals and crash reporting.

How can fonts cause CLS?

Let’s start at the beginning. When a user requests a webpage a number of things need to be fetched, including javascript, HTML, and fonts. That font may or may not be available on the user’s device. If the users visited the page before or already have the font on their device, the font won’t cause CLS. If they don’t, the browser will try to download it from the server. The way that the new font gets loaded matters a lot in terms of CLS. There are two situations that can happen: FOIT and FOUT.

Flash of Invisible Text (FOIT)

FOIT, or “Flash of Invisible Text” is when the browser loads the page without the text while the web font is being downloaded. Then it suddenly draws the page. This is considered a bad user experience because all of the written content is blocked during the time it takes to download the web font, which, depending on the device and internet speed could be a while. We definitely want to avoid this.

Flash of Unstyled Text (FOUT)

FOUT, or “Flash of Unstyled Text”, is when the browser uses a system font while the web font is being downloaded, and then it redraws the text with the web font once it’s available. There are going to be several differences between the system font and the web font. We’re talking about two different fonts after all. The bigger the difference is, the greater the layout shift is going to be. Let’s see what we can do to minimize it.

How to minimize CLS?

There are a few things we can do to minimize CLS, some of them are straightforward, but for others, we would need to make a decision either as individuals or as a team. Let’s check them out!

Don’t use a web font

I know, not really a tip, but I had to mention it. The built-in fonts aren’t actually that bad. If you’re building something where the font isn’t crucial, don’t opt-in to use a web font. Seriously, do yourself a favor and avoid doing all the tips below. There’s a cool website called modernfontstacks.com that showcases 15 different built-in font combinations that load instantly and don’t cause layout shifts. I’d check them out before making a decision. There’s a preview of the fonts below the list. Don’t forget to scroll down to it.

Use fewer font files

Maybe you’ve never paid attention to this. Or maybe the web designer designed the website in that way. Either way, it’s important to know that when you’re loading a font, you’re loading a font file for each weight and style you use. For example, if you’re using a regular font for the body, a bold font for the headings, a semibold for the links, and an italic font for the quotes, you’re actually loading 4 different font files. That’s 4 different HTTP requests. There’s an experimental tool in Chrome DevTools called CSS Overview that lists all of the used font weights on your page.

The browser is also capable of mimicking the weight and style of the font as well, but it might not always produce an accurate result, depending on the font. Check if the faux-bold and/or faux-italic looks “good enough”. If it does, don’t load that weight.

Use a variable size font

If you don’t want to depend on the browser’s capabilities to mimic the font weights and want to avoid loading multiple font files, you can consider using a variable font size. This way you only load a single font file, and the browser will know how to render the font in any weight you want. You can also control other properties of the font, like the slant, width, weight, optical size etc.

Host your own fonts

It’s definitely a lot easier importing a Google Font project than hosting your own and registering a font face. And it’s not just you. The same research from before shows us that of the 80% of websites that use web fonts, 65% of them use Google Fonts. But hosting your own fonts is better, and not as complex as it seems. The browser has already established a connection to your server. So when you tell it to download a font from a third-party service, it has to establish a new connection to that service, and go through the same process of DNS lookup, creating a TCP connection, TLS negotiation etc. Serving your font from your own server will cut down on the cost of having to establish a new connection. The time to get the font also depends on the availability of the service. If you can, consider serving your own fonts.

Use a woff2 font format

The format of the font also matters. If you’ve considered hosting your own fonts, you’ll have the option to pick the font format. Fonts come in multiple fonts, like ttf, otf, woff, woff2, eot etc. The first two aren’t meant to be used by the browser. The remaining ones do support subsetting, which is getting rid of the unused glyphs to reduce the file size. The eot font uses LZ compression, woff uses gzip, and woff2 uses brotli. Brotli produces the smallest file size, and it’s supported by all modern browsers.

Use the font-display property

The font-display property is a CSS property of @font-face that tells the browser how to handle the font while it’s being downloaded. It can be one of the following values:

  • auto: The font display strategy is up to the browser. It’s the default value.
  • block: It gives the font a short block period and an infinite swap period. We’ll talk about these in a bit.
  • swap: No block period and an infinite swap period.
  • fallback: Extremely small block period and a short swap period.
  • optional: Extremely small block period and no swap period.

The block period means that any element that uses a specific font must render an invisible fallback font while the correct font is loading. The swap period means that the element will render a visible fallback font while the correct font is loading, and then it redraws it with the correct font once it’s available. Font Loading Strategies You should probably avoid the block strategy. It has a really long block period without providing a fallback font, which means that the user will not be able to read your website for 3 seconds if the font takes more than that to load. The swap strategy is a good option, but you would need to make sure that the fallback font doesn’t differ too much from the web font. The next trick will help you with that. The fallback strategy hides the text for 100ms, then shows the fallback font and only shows the web font if it’s loaded within the first 3 seconds. The optional strategy is similar to the fallback, but it only uses the web font if it loads within the 100ms block period. If not, it uses the fallback font and does not swap it later on.

I’d say if the font is important, use the swap strategy because it ensures that the user will be able to read the text and the correct font is going to be used. You just need to make sure that the fallback font is similar enough to the web font. If the font is not important, use the fallback strategy. The blocking time is very short, and there’s no swapping later so you won’t need to worry about the layout shift. Cache the web font, so that the next page visit will have the web font already loaded. With these steps in place, there will be 0 layout shifts and the correct font will be loaded.

Define a fallback font with font descriptors

If you’ve decided to go with the swap strategy, you should spend some time fine-tuning the fallback font so it doesn’t cause too much of a layout shift. You can achieve that by using font descriptor properties like size-adjust, ascent-override, descent-override, and line-gap-override. Fine-tuning these values might be a tedious task, so be smart and use a tool like this codepen that visualizes the difference between your font and the fallback. Another option is Malte Ubl’s tool that generates the font descriptors for you, which you can then preview in the first tool and do some fine-tuning if necessary.

Here’s an example CSS of loading a self-served IBM Plex Serif font with a fine-tuned fallback:

/* Loading the web font */
@font-face {
  font-family: "IBM Plex Serif Regular";
  src: url("/fonts/IBMPlexSerif-Regular.woff2") format("woff2");
  font-weight: normal;
  font-style: normal;
  font-display: swap;
}
/* Defining the fallback */
@font-face {
  font-family: "IBM Plex Serif-fallback";
  size-adjust: 117.1%;
  ascent-override: 86%;
  src: local("Times New Roman");
}

/*
	Defining a css variable that combines the web font
  with the fallback and an alternative from modernfontstacks.com
*/
:root {
	--font-regular: "IBM Plex Serif Regular", "IBM Plex Serif-fallback",
      Rockwell, "Rockwell Nova", "Roboto Slab", "DejaVu Serif", "Sitka Small",
      serif;
}

/* Setting the font to the paragraph element */
p {
	font-family: var(--font-regular);
}

Improve the user experience with the right fonts

If you’re using web fonts your users are going to experience a layout shift, whether you like it or not. Spending time on picking the right strategy will help reduce the amount of CLS your users will experience. Reducing the number of web fonts used, choosing variable-size fonts, as well as selecting the right font file and display types will all help improve the user experience on your site. It’s also a really good idea to keep an eye on the CLS score in production because it might not be the one you see in DevTools. A performance monitoring tool like Sentry is perfect for keeping an eye on the CLS score in production, while also monitoring for other important metrics and web vitals.

Hopefully these tips will help you minimize the CLS caused by the web font loading and make the user experience better for your users. If you’re interested in learning more about using a performance monitoring tool, sign up here to start monitoring today.

Your code is broken. Let's Fix it.
Get Started

More from the Sentry blog

ChangelogCodecovDashboardsDiscoverDogfooding ChroniclesEcosystemError MonitoringEventsGuest PostsMobileMoonlightingOpen SourcePerformance MonitoringRelease HealthSDK UpdatesSentry
© 2024 • Sentry is a registered Trademark
of Functional Software, Inc.