Why do web font loading strategies that use font-display swap sometimes create CLS spikes that exceed the good threshold even with preloading?

The question is not whether font-display: swap prevents invisible text. It does. The question is why swapping from a fallback font to a web font triggers layout shifts that can push CLS past 0.1, even when you preloaded the font file and it arrived quickly. The shift occurs because the fallback font and the web font have different metrics — different x-height, character width, line height, and ascent/descent values. When the browser swaps fonts, text reflows: lines break at different points, paragraphs change height, and every element positioned relative to that text moves. Preloading accelerates delivery but does not eliminate the metric mismatch that causes the shift. This article explains the specific mechanism and the strategies that actually solve it.

The Font Metric Mismatch That Drives Layout Shifts

Every font defines a set of vertical and horizontal metrics that determine how text occupies space on the page. The critical metrics include ascent (the height above the baseline), descent (the depth below the baseline), line gap (additional spacing between lines), and average character width. System fallback fonts like Arial, Helvetica, Georgia, and Times New Roman were designed decades before the web fonts they now substitute for, and their metrics differ — sometimes substantially — from modern custom typefaces.

When font-display: swap triggers a font replacement, the browser re-renders all text using the new font’s metrics. If the web font’s characters are wider than the fallback’s, lines that previously fit in a single line now wrap to two. If the web font has a taller ascent or larger line gap, every text block’s computed height increases. Paragraphs grow, headings expand, and every element positioned below or relative to that text shifts downward. The Layout Instability API measures each of these movements as layout shifts.

HTTP Archive data indicates that over 80% of websites use web fonts, making font-swap CLS one of the most widespread sources of layout instability. Research into CLS attribution estimates that web font text reflow accounts for approximately 25% of all layout shifts, second only to images without reserved dimensions (approximately 60%) and ahead of dynamically inserted content like ads and widgets (approximately 15%).

The severity of the shift depends on the volume of visible text at the moment of the swap. A single heading produces a small shift. A full-screen article layout where the headline, subheadline, author byline, and opening paragraphs all use the custom font produces a shift large enough to fail the 0.1 threshold on its own. Multiple text elements swapping simultaneously accumulate within the same CLS session window.

Why Preloading Reduces but Does Not Eliminate the Swap Shift

Preloading with <link rel="preload" as="font" type="font/woff2" crossorigin> starts the font download during HTML parsing rather than waiting for the CSS to be parsed and the font reference to be discovered. This reduces the window during which the fallback font is visible. If the font arrives before the browser first paints text content, no swap occurs and the CLS contribution from fonts is zero.

But preloading provides a timing advantage, not a guarantee. On slower connections — particularly mobile 3G connections where CrUX captures the 75th percentile that determines the page experience signal — the font file may not arrive before first paint even with preloading. A 40KB WOFF2 file that downloads in 50ms on broadband takes 300-500ms on a throttled mobile connection. If the browser paints text with the fallback font at t=200ms and the preloaded web font arrives at t=400ms, the swap still occurs at t=400ms, producing the same metric-mismatch shift as without preloading — just 200ms sooner.

The practical effect of preloading is that it reduces the percentage of users who experience the font swap, particularly those on fast connections. But the slowest 25% of users — the population whose experience defines the 75th percentile CrUX score — may still see the swap. For sites where the 75th percentile CLS is near the 0.1 boundary, preloading alone may not be sufficient to consistently pass.

Font file size also determines preloading effectiveness. A variable font file containing multiple weights and styles can exceed 100KB in WOFF2 format, which is large enough that even preloaded delivery on moderate connections does not complete before first paint. Subsetting the font to include only the character ranges actually used on the page reduces file size and improves the probability that the preloaded font arrives before the fallback renders.

CSS font-face Descriptor Overrides for Swap-Induced Shifts

The @font-face descriptors ascent-override, descent-override, line-gap-override, and size-adjust provide the definitive solution to font-swap CLS. These properties modify the fallback font’s rendering metrics to match the web font before the swap occurs. When the fallback font renders with metrics identical to the incoming web font, the swap produces zero layout shift because the text occupies the same space in both font states.

The implementation defines a custom fallback @font-face with the system font as the source and the override properties applied:

@font-face {
  font-family: 'Inter';
  src: url('/fonts/inter.woff2') format('woff2');
  font-display: swap;
}

@font-face {
  font-family: 'Inter Fallback';
  src: local('Arial');
  size-adjust: 107.64%;
  ascent-override: 90.49%;
  descent-override: 22.48%;
  line-gap-override: 0%;
}

body {
  font-family: 'Inter', 'Inter Fallback', sans-serif;
}

Cross-Browser Behavior and Variable Font Metric Challenges

The size-adjust property scales the fallback font’s glyphs to match the web font’s average character width. The ascent-override and descent-override properties adjust the vertical space allocation above and below the baseline. The line-gap-override controls the additional inter-line spacing. When these four values are correctly calculated, the fallback text occupies the same bounding boxes as the web font text, and the swap causes no element movement.

Calculating the correct override values requires comparing the metric tables of the fallback font and the web font. Tools like Fontaine, Capsize, and the Chrome team’s fallback font generator automate this comparison. Framework integrations in Next.js (next/font) and Nuxt.js (@nuxtjs/fontaine) generate these overrides at build time, ensuring they stay synchronized with the font files.

One important limitation: font metric overrides address vertical spacing and overall character scaling but do not control letter-spacing or word-spacing, which are not available as @font-face descriptors. If the web font has significantly different character-level spacing than the fallback, individual lines may still break at different points, producing height changes in text blocks. This residual shift is typically much smaller than the unmitigated swap shift but is not zero for fonts with substantially different character widths.

Operational Complexity of Font Metric Overrides Across Families

Font metric overrides must be recalculated whenever the web font file changes. A font update from the type foundry, the addition of a new weight to the font family, or a variable font axis modification alters the metrics that the overrides are tuned to match. Stale override values produce a different kind of CLS: the fallback and web font now occupy subtly different space, creating a small but measurable shift that may accumulate across multiple text elements.

The override values are also specific to the fallback font in the stack. Overrides tuned for Arial as the fallback do not produce correct results if the browser selects Helvetica, Roboto, or another system font. The fallback font selection depends on the user’s operating system and installed fonts: Windows defaults to Arial, macOS may use Helvetica Neue or San Francisco, and Android uses Roboto. If the override is tuned for Arial but a macOS user’s browser selects Helvetica Neue, the metrics will not match precisely.

This platform-dependent fallback selection means that truly zero-CLS font loading requires either targeting the most common fallback across the user base (typically Arial, which covers Windows and Android) and accepting small residual shifts on other platforms, or defining multiple fallback @font-face declarations with platform-specific overrides. The second approach is more accurate but proportionally more complex to maintain.

The font-display optional Alternative and Its Tradeoffs

Build-time automation is the practical requirement for production sites. Manual calculation and maintenance of override values across font updates, platform targets, and multiple font families is unsustainable. Fontaine and framework-integrated tools like next/font handle this automatically, extracting metrics from font files during the build process and generating the corresponding CSS overrides. Without this automation, font metric overrides become technical debt that degrades as fonts evolve.

font-display: optional instructs the browser to use the web font only if it is already available (from cache or an extremely fast download) within an approximately 100ms window. If the font does not arrive in time, the browser uses the fallback for the entire page lifecycle — no swap occurs. This guarantees zero CLS from font loading because there is never a font replacement event.

The tradeoff is typographic inconsistency. Some users see the custom font (those with cached files or fast connections) while others see the system fallback. For brand-critical hero text or sites where typographic identity is central to the design, this inconsistency may be unacceptable. For body text on content-heavy pages where readability matters more than brand expression, the tradeoff is typically favorable.

The optional strategy also means that first-time visitors almost never see the custom font, since the font file must complete downloading within the 100ms window to be used. On subsequent visits, the cached font loads instantly and the custom typography renders. This creates a “first visit fallback, repeat visit custom” pattern that some design teams find acceptable and others do not.

Combining font-display: optional with font metric overrides provides the best of both approaches: zero CLS guaranteed (because either no swap occurs or the swap is metrically identical), with the visual appearance of the fallback font closely matching the web font for first-time visitors. This combination eliminates the inconsistency problem while maintaining CLS compliance, making it the most robust strategy for sites that must pass CLS reliably at the 75th percentile while maintaining reasonable typographic quality.

Does using a single variable font file reduce CLS compared to loading multiple font weight files?

Not directly. CLS from font-display: swap occurs because of metric differences between the fallback font and the web font, regardless of how many font files load. A variable font reduces total network requests and bandwidth, which can make the font available sooner and shorten the swap window, but the swap shift magnitude depends on metric alignment. Applying font metric overrides to the fallback font is what actually reduces the shift size.

Can system font stacks eliminate font-related CLS entirely?

Yes. System font stacks avoid any font swap event because no external font file loads. There is no fallback-to-web-font transition, so no layout shift occurs. The tradeoff is losing custom typography. For sites where CLS compliance is critical and brand typography is secondary, system font stacks guarantee zero font-related CLS with no implementation complexity.

Does preconnecting to the font CDN reduce font-swap CLS more than preloading the font file?

No. Preconnect establishes the connection early but does not begin downloading the font file. Preload initiates the actual file download during HTML parsing, making the font available sooner and narrowing the window during which the fallback font displays. Preconnect is useful as a supplement to preload when the font is hosted on a third-party domain, but preload alone has a larger impact on reducing the swap duration that causes CLS.

Sources

Leave a Reply

Your email address will not be published. Required fields are marked *