Fixing CLS Caused by Web Fonts: A Real Core Web Vitals Case Study

Cumulative Layout Shift (CLS) is often described as a “layout problem,” but in practice, it is frequently a font problem. This case study documents how a seemingly correct setup still produced a CLS score close to 1.0, and how fixing font metrics and layout stability reduced CLS to near zero without changing design or content.


The Problem: CLS Near 1.0 on Mobile

Lighthouse reported a CLS score of approximately 0.93 on mobile. The report pointed clearly to two main contributors:

  • Web fonts loaded from fonts.gstatic.com

  • Minor layout shifts inside a blog list and its cards

What made this case confusing was that best practices already appeared to be in place. Fonts were using font-display: swap, images had reasonable dimensions, and there were no obvious DOM insertions during load.

Despite this, the layout shifted dramatically once the fonts finished loading.


Why font-display: swap Was Not Enough

The key misunderstanding in many CLS cases is assuming that font-display: swap alone prevents layout shifts. In reality, it only ensures that text is visible early. It does not guarantee that the fallback font and the final font share the same metrics.

In this case, the fallback font had different ascent, descent, and line gap values compared to the final font. When the real font loaded, text height changed, pushing the entire blog list downward. Because this happened after initial rendering, Lighthouse recorded it as a large layout shift.


Diagnosing the Root Cause

The Lighthouse CLS breakdown highlighted .woff2 font files as the primary source of layout shifts. Enabling Layout Shift Regions in Chrome DevTools made the issue obvious: nearly the entire page moved when the web font replaced the fallback font.

This confirmed that the problem was not JavaScript or late content insertion, but font metric mismatch.


The Core Fix: Font Metrics Overrides

The most effective fix was adding font metrics overrides directly to the @font-face declaration. This forces the browser to treat the fallback font as having metrics similar to the final font.

A simplified example of the solution:

@font-face {
  font-family: 'Inter';
  src: url('/fonts/inter-regular.woff2') format('woff2');
  font-weight: 400;
  font-style: normal;
  font-display: swap;

  ascent-override: 90%;
  descent-override: 22%;
  line-gap-override: 0%;
  size-adjust: 100%;
}

With this in place, the fallback text and the final font occupy nearly the same vertical space. When the font loads, the text no longer causes layout movement.


Choosing the Right Fallback Fonts

Another important improvement was explicitly defining fallback fonts instead of relying on the browser default.

 
body {
  font-family: 'Inter', system-ui, -apple-system, 'Segoe UI', Roboto, Arial, sans-serif;
}

This ensures more predictable typography during the initial render and further reduces the chance of metric mismatch.


Stabilizing the Blog List Layout

Although fonts were the main cause, the blog list layout also contributed small shifts. The height of each card depended on text rendering, so even small font changes affected the overall layout.

To address this, a minimum height was added to each card:

 

.blog-card {
  min-height: 160px;
}

For cards containing images, an explicit aspect ratio was defined:

.blog-card img {
  width: 100%;
  aspect-ratio: 16 / 9;
  height: auto;
}

These changes ensured that content space was reserved before fonts and images finished loading.


The Result

After applying these fixes:

  • CLS dropped from ~0.93 to near zero

  • Lighthouse no longer flagged font files as layout shift sources

  • The blog list remained visually stable during load

  • No UI or content changes were required

The most important outcome was that the fix addressed the root cause rather than masking the symptoms.


Key Takeaways

CLS issues are often misdiagnosed as layout or JavaScript problems when the real cause is typography. If your CLS score is high and Lighthouse highlights font files, focus on font metrics before anything else.

In many cases, font metrics overrides combined with stable layout sizing are enough to eliminate CLS entirely.

 
← Back to Blog