Skip to Content
HTML and CSS Advanced: Styles That Hold Up in Production
Advanced
0 likes
Courses
HTML and CSS Advanced: Styles That Hold Up in Production

Font Loading Without Layout Jump

Choose a font-display value, preload the right file, and build a fallback that takes the same room as the web font. After this, the swap stops moving your text.

Course content

Sign in to save your progress.

Course Home

Skip ahead: If you already preload your main font, set font-display, and know what size-adjust does on a fallback, skip to "What I measured", then continue.

A web font arrives after the text does. The browser has to draw something in the meantime, so it uses a fallback font and then swaps. If the two fonts have different widths and line heights, the text reflows when the swap happens, and everything below it jumps. That jump is layout shift, and it is the thing you can fix.

Decide what happens while the font loads

@font-face {
  font-family: "Demo Sans";
  src: url("/fonts/demo-sans.woff2") format("woff2");
  font-display: swap;
}
font-displayWhile loadingAfter it arrives
swapAlmost no wait, fallback shownAlways swaps
fallbackAlmost no wait, fallback shownSwaps only within a short window
optionalAlmost no waitDoes not swap if it arrived late; next page uses it
blockText hidden for a short periodSwaps

swap is the usual choice for body text. optional suits a decorative font you can live without. block hides text, so I would avoid it for anything people have to read.

Load it earlier

<link rel="preload" href="/fonts/demo-sans.woff2" as="font" type="font/woff2" crossorigin>

Preload starts the download when the HTML is parsed, instead of when the browser first meets text that uses the font. The crossorigin attribute is required for fonts, even from your own domain. Without it the browser fetches the font twice. Preload only the one or two files the first screen needs.

Make the fallback match

@font-face {
  font-family: "Demo Sans Fallback";
  src: local("Arial");
  size-adjust: 114.4%;
  ascent-override: 81.1%;
  descent-override: 20.6%;
  line-gap-override: 0%;
}

body { font-family: "Demo Sans", "Demo Sans Fallback", Arial, sans-serif; }

This defines a fallback that is Arial, scaled and re-spaced to take the same room as the web font. The browser shows it first, then swaps with almost no reflow. I got the numbers from the font files: the width of a sample sentence in each font gives size-adjust, and the font's ascent and descent divided by that factor give the overrides.

What I measured

The test font was DejaVu Sans, served 0.8 seconds late, under two paragraphs and a box. I recorded layout-shift entries in Chrome 154 with a PerformanceObserver.

font-plain     CLS 0.0165
font-adjusted  CLS 0

The page without the adjusted fallback shifted by 0.0165. The page with it did not shift at all. On the test machine, Arial resolved to Liberation Sans, which is metric-compatible, so local("Liberation Sans") was added in the demo as a second source. On Windows and macOS local("Arial") is the one that applies.

⚠ Warning: Compute the overrides from the actual font file, not from a blog post's table. A different weight or a subset can change the metrics.

Common mistakes

  • Preloading five fonts. Each one competes with the HTML and CSS for the first seconds.
  • Loading a weight you never use, such as bold italic for one heading.
  • Serving TTF or OTF when WOFF2 is available. It is a smaller file for the same font.
  • Setting font-display: swap and calling it done. Swap only decides what shows, and not how much the page moves.

💡 Tip: Subset fonts to the languages you actually publish. A Latin-only subset is far smaller than a full family.

What happens if you skip this

Every visit starts with text in one font and then a visible jump into another. On a slow connection the jump lands after the reader started reading, so they lose their place. Layout shift is also one of Google's Core Web Vitals, so it can show up in how your pages are measured.

Try it

Record a Performance trace of your own page on a throttled connection and open the Layout Shifts track. If a shift lines up with the font arriving, build a fallback like the one above and record again.

Which font on your site is worth a jump in the layout?

About

Choose a font-display value, preload the right file, and build a fallback that takes the same room as the web font. After this, the swap stops moving your text.

Duration
5 min
Last update
08/08/2026
Was this helpful?
​ 0 ​ 0

Comments (0)

Sign in to comment

No comments yet.