đ Executive Summary
TL;DR: Web fonts often cause Flash of Unstyled Text (FOUT) and Cumulative Layout Shift (CLS) due to delayed loading, negatively impacting user experience and Core Web Vitals. To solve this, optimize @font-face by preloading critical self-hosted fonts using to prioritize fetching, or use font-display: swap for a quick fix, effectively taming layout shifts.
đŻ Key Takeaways
- The
font-display: swap;property offers a quick fix for FOUT by immediately displaying a fallback font, but it does not eliminate the subsequent CLS. - Using
in the HTMLis the recommended permanent solution for prioritizing critical self-hosted web font downloads, effectively preventing both FOUT and CLS. - The
crossoriginattribute is crucial when preloading fonts, even for self-hosted ones, to prevent browsers from fetching the font file twice due to CORS requirements.
Tired of your site flashing ugly system fonts on load? This is a no-nonsense guide for DevOps and front-end engineers to finally tame @font-face, stop layout shifts, and get your web fonts loading like they should.
Letâs Talk About @font-face: A DevOps War Story on Taming FOUT and CLS
I remember a launch a few years back. It was for a major client, one of those âall hands on deck, CEO is watchingâ situations. The marketing team had just signed off on a gorgeous, but hefty, new brand font. The front-end team implemented it, and everything looked pixel-perfect on their local machines. Then we pushed it to staging. The site loaded, and for a solid second and a half, every heading was in Times New Roman before snapping into the brand font. The whole page would reflow. It looked broken, cheap. My pager lit up with alerts from our synthetic monitoring tools: âCumulative Layout Shift (CLS) score has degraded by 300% on prod-web-01â. It was a nightmare. This, my friends, is the all-too-common pain of poorly optimized web fonts.
The âWhyâ: The Browser is Doing Exactly What You Told It To
This isnât a bug; itâs a feature, a really annoying one. The root of the problem is the browserâs rendering process. Itâs a race. The browser parses your HTML, sees a link to your CSS, and starts fetching it. While thatâs happening, it starts painting what it can. It reads your CSS and sees font-family: 'Super-Brand-Font', sans-serif;. The browser says, âOkay, I donât have âSuper-Brand-Fontâ yet, so Iâll use the fallback sans-serif for now. No big deal.â It renders the page. Then, a second later, the actual WOFF2 font file finally downloads from asset-cdn-prod-01. The browser then shouts, âIâve got it!â and dutifully re-renders all the text, causing that jarring flash and layout shift. Youâre seeing a Flash of Unstyled Text (FOUT) and itâs killing your Core Web Vitals.
So, how do we fix it? We have a few tools in our belt, ranging from a quick fix to a full-on architectural change.
Solution 1: The Quick Fix â Just Tell It to Swap
The fastest thing you can do is add a single property to your @font-face declaration: font-display: swap;. This is you explicitly telling the browser, âLook, I know this is going to take a second. Just use the fallback font immediately, and when the real one arrives, swap it in.â
@font-face {
font-family: 'Open Sans';
src: url('/fonts/OpenSans-Regular-webfont.woff2') format('woff2');
font-weight: 400;
font-style: normal;
font-display: swap; /* <-- THIS IS THE MAGIC */
}
The Good: Itâs one line of code. It ensures your content is readable immediately. For many, this is âgood enough.â
The Bad: It doesnât solve the FOUT or the CLS. It just accepts it. Youâre still going to see that flash; youâre just controlling the behavior slightly better. Itâs a band-aid, not a cure.
Solution 2: The Permanent Fix â Preload Your Critical Fonts
If you want to actually solve the problem, you need to change the browserâs priorities. The browser doesnât know your font file is critical until it parses the CSS that requests it. We can give it a hint way earlier by using <link rel="preload"> in the <head> of your HTML.
This tells the browser, âHey! Stop what youâre doing. This font file is extremely important for the initial render. Go fetch it. Now.â
<!-- In your HTML's <head> -->
<link rel="preload" href="/fonts/Super-Brand-Font-Bold.woff2" as="font" type="font/woff2" crossorigin>
Pro Tip: The
crossoriginattribute is crucial here, even if you are self-hosting the font. Fonts are fetched using an anonymous mode CORS request, and preloading needs to match this behavior, or the browser will fetch the font twice! Iâve seen teams burn hours debugging that one.
This approach works best when you are self-hosting your fonts. If youâre pulling from a third-party like Google Fonts, youâre adding DNS lookups, TLS handshakes, and another point of failure. Download those WOFF2 files, put them on your own CDN, and preload them. This gives you control and is almost always my recommended approach.
Solution 3: The âNuclearâ Option â Subset and Inline
Sometimes, even preloading isnât fast enough. Maybe you have a hero heading with âWelcome!â that absolutely, positively cannot shift. For this, we get serious. This is the âI need zero CLS above the fold, and I donât care what it takesâ option.
The process has two steps:
-
Subset the font: A full font file contains hundreds of characters you donât need for a single heading. Use a tool (like
glyphhanger) to create a tiny version of the font that ONLY contains the characters you need (e.g., W-e-l-c-o-m-!). The file size will be minuscule. -
Inline the font: Convert that tiny font file to a Base64 string and embed it directly in your CSS inside a
<style>tag in the HTML<head>.
<!-- In your HTML's <head> -->
<style>
@font-face {
font-family: 'Super-Brand-Hero';
src: url(data:font/woff2;base64,d09GMgABAAAAAAbcAA...[very long string]...) format('woff2');
font-weight: 700;
font-style: normal;
font-display: block; /* Use 'block' here to wait a tiny moment for it to be ready */
}
</style>
The Good: The font is available *instantly* with the HTML. There is no separate network request. The browser has it before it even needs it. This guarantees zero font-related CLS for that specific text.
The Bad: This is a maintenance nightmare. If the text changes, you have to repeat the process. It also increases the size of your initial HTML document, which can delay the first paint if you overdo it. This is a scalpel, not a hammer. Use it only for the most critical, visible-on-load text.
My Final Take
For 90% of cases, self-hosting your fonts and preloading the critical weights (e.g., regular and bold) is the sweet spot. It gives you a massive performance win without the maintenance headache of inlining. Start there. Donât let a fancy font be the reason your beautifully engineered application feels slow and broken.
đ Read the original article on TechResolve.blog
â Support my work
If this article helped you, you can buy me a coffee:

Top comments (0)