patternjavascriptModerate
Font loading strategies: font-display and preventing FOIT/FOUT
Viewed 0 times
font-displayFOITFOUTweb fontssize-adjustascent-overridefont loadingCLS
Problem
Custom web fonts cause Flash of Invisible Text (FOIT) while the font loads, or Flash of Unstyled Text (FOUT) when the fallback is swapped for the loaded font. Both hurt perceived performance and can contribute to CLS.
Solution
Use font-display: optional for critical text (zero layout shift, no FOUT) or font-display: swap with size-adjust to minimize the layout shift during swap.
/ font-display: optional — best for LCP text, no FOUT /
@font-face {
font-family: 'MyFont';
src: url('/fonts/myfont.woff2') format('woff2');
font-display: optional;
}
/ font-display: swap + size-adjust — good UX, minimal CLS /
@font-face {
font-family: 'MyFont';
src: url('/fonts/myfont.woff2') format('woff2');
font-display: swap;
}
/ Adjust fallback font to match custom font metrics /
@font-face {
font-family: 'MyFont-fallback';
src: local('Arial');
size-adjust: 105%;
ascent-override: 90%;
descent-override: 22%;
}
body { font-family: 'MyFont', 'MyFont-fallback', sans-serif; }
/ font-display: optional — best for LCP text, no FOUT /
@font-face {
font-family: 'MyFont';
src: url('/fonts/myfont.woff2') format('woff2');
font-display: optional;
}
/ font-display: swap + size-adjust — good UX, minimal CLS /
@font-face {
font-family: 'MyFont';
src: url('/fonts/myfont.woff2') format('woff2');
font-display: swap;
}
/ Adjust fallback font to match custom font metrics /
@font-face {
font-family: 'MyFont-fallback';
src: local('Arial');
size-adjust: 105%;
ascent-override: 90%;
descent-override: 22%;
}
body { font-family: 'MyFont', 'MyFont-fallback', sans-serif; }
Why
font-display: optional tells the browser to use the fallback if the font is not in cache, eliminating FOUT entirely at the cost of occasionally showing the fallback. size-adjust closes the metric gap between the custom font and fallback, making the FOUT visually imperceptible.
Gotchas
- Self-host fonts to avoid extra DNS lookups and connection overhead from Google Fonts CDN
- Preload the woff2 font file with <link rel="preload" as="font" crossorigin> to reduce FOIT duration
- font-display: block (browser default for @font-face) shows invisible text for up to 3 seconds — avoid it
- Variable fonts reduce font file count and requests but may be larger than a single weight file
Code Snippets
Font Loading API with class-based fallback
// Use Font Loading API to detect when font is ready
document.fonts.load('1em MyFont').then(() => {
document.documentElement.classList.add('fonts-loaded');
});
// CSS: apply font only after it is loaded
body { font-family: sans-serif; }
.fonts-loaded body { font-family: 'MyFont', sans-serif; }Context
When custom fonts are causing visible text flashes or contributing to CLS score
Revisions (0)
No revisions yet.