Custom font impact on CLS

Custom font impact on CLS

Recently I wrote about pre-loading fonts to improve CLS (Cumulative Layout Shift) scores and while that will go a long way to improving CLS as well as limiting the amount of flicker your users will experience. It still leaves an issue that not all fonts are the same size, and therefore can take up varying amounts of space.

Take a look at these two examples of a heading that contains the same text and font size but with different font family's.

Custom font heading
Base font heading on 2 lines

The first is using a custom font that requires a download to happen before it is shown, while that happens the second will be displayed.

As you can see the custom font has much narrower letters meaning more text will fit on each line, whereas the base font goes onto two lines and creates CLS when the fonts swap.

Fixing Line Heights

As well as widths fonts can have different heights, fortunately, this is relatively simple to fix by including line heights in your CSS.

1.customFont {
2 font-size: 24px;
3 line-height: 1rem;
4 font-family: bebasneuepro-bold, sans-serif;
5}

Fixing Font Widths with Size-Adjust

It's not possible to make one font the exact size of another, but we can get close using size adjust.

Size-adjust allows you to set a percentage to scale a font by and can be applied on a font-face definition. This will affect both the height and width of the font, but if you've fixed the line height then the font getting smaller in height won't make the overall content shorter. With size adjust, we are aiming to match the horizontal spacing so that line breaks happen at the same point, leaving us with an equal amount of lines irrespective of font and therefore no CLS.

An important aspect is that we are creating a font-face for a local font that will load instantly. As you can see my custom font loads from a URL and I've created a fallback font-face loading san-serif from local.

The custom font class now includes my fallback font in the font-family rather than sans-serif directly.

1 @font-face {
2 font-family: 'bebasneuepro-bold';
3 src:
4 url('/fonts/bebasneuepro-bold-webfont.woff2') format('woff2'),
5 url('/fonts/bebasneuepro-bold-webfont.woff') format('woff');
6 font-display: swap;
7 }
8
9 @font-face {
10 font-family: bebasneuepro-bold-fallback;
11 src: local('sans-serif');
12 size-adjust: 62%;
13 }
14
15.customFont {
16 font-size: 24px;
17 line-height: 1rem;
18 font-family: bebasneuepro-bold, bebasneuepro-bold-fallback;
19}

The effect is the heading text now takes up the same width irrespective of font and stays on one line. As the line height has also been set the overall height of the element stays the same and there is no CLS impact.