DEV Community

Joseph Anady
Joseph Anady

Posted on • Originally published at thatdevpro.com

Self-Hosting Google Fonts with size-adjust: Zero CLS Web Font Swap

Web font swap is the silent CLS killer in 2026. The fallback paints in Georgia at one x-height, the web font swaps in at a different x-height, and every line of text shifts by a few pixels. Lighthouse catches it. Google ranks lower for it. The fix is six lines of CSS most engineers never see.

Short answer: Self-host the font, declare a synthetic fallback face with size-adjust, ascent-override, and descent-override matched to the web font, and reference the synthetic fallback in your font cascade after the real font name.

Why font-display: swap hurts CLS

The browser paints text immediately using whichever fallback the cascade names. When the web font finishes loading, the swap happens, and every line of text re-flows. Lighthouse logs the shift as cumulative layout shift, the ranking signal Google has been folding into search results since the Page Experience update.

A real client site I audited last week was running CLS 0.135 from a single web font swap. Forty-five minutes after publishing the fix, the same page measured CLS 0.000.

The three-step fix

Step 1: Self-host the font

<link rel="preload" href="/assets/fonts/inter-400.woff2" as="font"
      type="font/woff2" crossorigin>
Enter fullscreen mode Exit fullscreen mode

Self-hosting kills the third-party DNS lookup, the third-party TLS handshake, and the privacy compliance dance with Google Fonts.

Step 2: Declare a synthetic fallback @font-face

This is the part most engineers miss. The @font-face rule can point at a local font (Georgia, Arial) and override that font intrinsic metrics to match the web font you are eventually swapping in.

@font-face {
  font-family: 'Inter Fallback';
  src: local('Arial');
  ascent-override: 90.49%;
  descent-override: 22.56%;
  line-gap-override: 0%;
  size-adjust: 107.2%;
}
@font-face {
  font-family: 'Playfair Display Fallback';
  src: local('Georgia');
  ascent-override: 96.7%;
  descent-override: 21.4%;
  line-gap-override: 0%;
  size-adjust: 109.5%;
}
Enter fullscreen mode Exit fullscreen mode

Those exact percentages come from the Next.js next/font/google generator.

Step 3: Insert the synthetic fallback in the cascade

--font-body: 'Inter', 'Inter Fallback', system-ui, sans-serif;
--font-display: 'Playfair Display', 'Playfair Display Fallback', Georgia, serif;
Enter fullscreen mode Exit fullscreen mode

The browser resolves Inter (web font) -> Inter Fallback (synthetic with corrected metrics) -> system fonts. When Inter loads, the swap is layout-neutral.

Verification

Open PageSpeed Insights. Run mobile audit. CLS reports 0.000 for any page whose layout shift was driven by font swap.

If CLS is still nonzero, the culprit is something else: late-loading images without width/height, content injected above existing content, or animation keyframes that touch layout properties.

Wider implications

Core Web Vitals is one of the lighter-weight ranking signals Google ships, but for sites in competitive verticals it is the difference between top three and page two. Every ThatDevPro engagement ships with font-swap CLS at zero. It is not a paid add-on. It is baseline.

For the full version with the live code on a real client site, see the original at thatdevpro.com.


Posted from ThatDevPro, SDVOSB veteran-owned web development and AI engineering studio. Originally diagnosed and shipped on the ThatDeveloperGuy Debian + nginx stack.

Top comments (0)