The Impact of Font Display Swap on CLS
Web fonts can cause layout shifts and invisible text. Learn how font-display values work and how to load fonts without hurting your CLS score.
Custom web fonts make your site look beautiful but can wreak havoc on performance. They cause two specific problems: invisible text (FOIT) and layout shifts (FOUT) — both of which hurt your Core Web Vitals.
The Font Loading Problem
When the browser encounters text styled with a custom font that hasn't loaded yet, it has two choices:
FOIT (Flash of Invisible Text)
Hide the text until the font loads. Users see a blank space where text should be. If the font takes 3 seconds, users stare at invisible text for 3 seconds.
FOUT (Flash of Unstyled Text)
Show the text with a fallback font immediately, then swap to the custom font when it loads. This is better for performance but causes a layout shift because fallback fonts have different metrics (size, spacing, line height).
The font-display Property
CSS gives you control over this behavior:
@font-face {
font-family: 'BrandFont';
src: url('/fonts/brand.woff2') format('woff2');
font-display: swap; /* or auto, block, fallback, optional */
}
Values Explained
| Value | Behavior | FCP Impact | CLS Impact |
|---|---|---|---|
auto |
Browser decides (usually block) |
❌ Bad | Varies |
block |
Hide text for up to 3s | ❌ Bad | ✅ Good |
swap |
Show fallback immediately, swap when ready | ✅ Good | ❌ Bad |
fallback |
Brief block (100ms), then fallback, short swap window | ✅ OK | ✅ OK |
optional |
Brief block (100ms), then fallback. Font only used if already cached | ✅ Best | ✅ Best |
Why swap Causes CLS
When you use font-display: swap, the browser shows text with the system fallback font immediately. When the custom font loads, it swaps in — and because the two fonts have different:
- Character widths — text lines break differently
- Line heights — paragraphs have different heights
- Letter spacing — words take up different horizontal space
The swap causes a layout shift. A paragraph that was 5 lines with the fallback font might become 4.5 lines with the custom font, shifting everything below it.
The Best Solutions
1. Use font-display: optional
The best option for CLS. The browser gives the font 100ms to load. If it's not ready, the fallback is used for the entire page lifecycle. The custom font is cached for the next navigation.
@font-face {
font-family: 'BrandFont';
src: url('/fonts/brand.woff2') format('woff2');
font-display: optional;
}
Trade-off: First-time visitors might not see your custom font. Repeat visitors will.
2. Use swap with Size-Adjusted Fallback
Match the fallback font metrics to your custom font so the swap doesn't cause layout shifts:
@font-face {
font-family: 'BrandFont Fallback';
src: local('Arial');
ascent-override: 90%;
descent-override: 22%;
line-gap-override: 0%;
size-adjust: 105%;
}
@font-face {
font-family: 'BrandFont';
src: url('/fonts/brand.woff2') format('woff2');
font-display: swap;
}
body {
font-family: 'BrandFont', 'BrandFont Fallback', sans-serif;
}
Tools to calculate these values:
- Fontaine — automatic fallback generation
- Font Style Matcher — visual comparison tool
3. Preload Critical Fonts
Make the font start downloading before the CSS is even parsed:
<link rel="preload" href="/fonts/brand.woff2" as="font"
type="font/woff2" crossorigin>
This reduces the time between FCP (with fallback) and font swap, minimizing the visible shift.
4. Self-Host Your Fonts
Google Fonts requires an extra DNS lookup and connection. Self-hosting eliminates that delay:
/* Instead of @import from Google Fonts */
@font-face {
font-family: 'Inter';
src: url('/fonts/inter-var.woff2') format('woff2');
font-display: optional;
font-weight: 100 900;
}
5. Use Variable Fonts
One variable font file replaces multiple static font files (regular, bold, italic), reducing total font bytes:
@font-face {
font-family: 'Inter';
src: url('/fonts/Inter-Variable.woff2') format('woff2-variations');
font-weight: 100 900;
font-display: optional;
}
Measuring Font-Related CLS
Chrome DevTools
- Open DevTools → Performance
- Record a page load
- Look for Layout Shift events
- Check if shifts coincide with font loading (Network tab)
Lighthouse
Lighthouse flags font issues in two audits:
- "Ensure text remains visible during webfont load"
- "Avoid large layout shifts"
The Recommended Stack
For the best balance of aesthetics and performance:
- Self-host your fonts (no external requests)
- Use WOFF2 format (best compression)
- Use variable fonts (one file, all weights)
- Set
font-display: optionalor use size-adjusted fallbacks withswap - Preload the primary font file
- Subset fonts to include only characters you need
Monitor Font Performance
Font-related CLS often appears after design updates or font changes. BadPageSpeed tracks CLS over time so you catch font-related regressions immediately.
Ready to stop wasting ad spend?
Track your landing page performance, monitor Core Web Vitals, and calculate exactly how much slow pages cost you.
Start Free — No Credit Card