Font Optimization for Your Own Store: System Fonts, font-display, Preloading, and FOIT

Fonts are the most overlooked performance bomb on any store you run yourself. An unoptimized web font file is often 300KB to 2MB, and while it loads the browser hides your text (FOIT) — on 3G that means a blank first screen for 3 seconds...

字体加载四态:FOIT、FOUT、swap与系统字体的取舍Font loading: FOIT vs FOUT vs swap vs system font stack 用Web字体会让首屏文字慢300至1000毫秒 —— B2B站真的值吗Web fonts delay text 300-1000ms — is it worth it on a B2B site 策略Strategy 首屏文字First-paint text 适用判断When to use 系统字体栈System stack 立即显示Instant 大多数B2B站默认选它Default for most B2B font-display:swapfont-display:swap 先fallback后替换Fallback then swap preload+swappreload + swap fallback后快速替换Fast swap after preload 品牌站首屏标题Brand hero headings font-display:blockfont-display:block 3秒内空白(FOIT)3s invisible (FOIT) 核心规则:自定义字体≤2个家族 · 每家族只加载400和700 · 子集化Rule: ≤2 families · weights 400+700 only · subset 中文Web字体几乎不可行 —— 一个中文字体2至8MB,系统字体栈是正确答案CN web fonts impractical (2-8MB) — use system stack 红线:字体文件没子集化就上全站 —— 中文一个文件5MB起步Red line: shipping unsubsetted CJK font (5MB+) font-display:block在3G下等于首屏3秒空白,访客早走了block on 3G = 3s blank screen, visitor already gone
Four font loading strategies compared: system fonts, swap, preload+swap, and block

Font Optimization for Your Own Store: System Fonts, font-display, Preloading, and FOIT

Bottom line first: Fonts are the most overlooked performance bomb on any store you run yourself. An unoptimized web font file is often 300KB to 2MB, and while it loads the browser hides your text (FOIT) — on 3G that means a blank first screen for 3 seconds. The right approach has three layers: English sites should use at most 2 web font families, load only the 400 and 700 weights of each, and always set font-display:swap; Chinese sites should almost never use web fonts and should go straight to a system font stack, because even a subsetted Chinese font file is still 2 to 8MB. One counterintuitive call: a decorative custom font does almost nothing for conversion on a B2B site and drags down LCP, so the system font stack is the default answer.

1. Why fonts block text from showing

Until the browser has the font file, it doesn’t know what the text should look like, and its default behavior is to hide the text (FOIT, Flash of Invisible Text). In older browsers that hidden period was 3 seconds; newer browsers cut it to 1 second and then force a fallback font. The problem: the body text on your first screen is the LCP element, so if it’s waiting on a font, LCP is held back by the font download.

A typical mistake on stores you run yourself: to add “design polish,” the theme author loads every weight of Inter or Roboto from Google Fonts (100 through 900, 9 weights), each 100KB, so one load pulls 900KB. On 3G the fonts alone take 3 to 5 seconds to download, and users see a blank screen. For the font-related Core Web Vitals metrics, see Core Web Vitals Optimization, and for how to set up your CDN, see CDN global acceleration.

Issue Performance impact Typical size
Loading all weights (9) 800KB extra on the first screen 80 to 120KB per weight
Unsubsetted Chinese font 2 to 8MB per file Far over the first-screen budget
font-display defaults to block FOIT hides text for 1 to 3 seconds Text is invisible
Key fonts not preloaded Font discovered late, 200ms delay LCP is slowed down
Using many families (more than 3) Every family has to download 300 to 500KB total

2. The system font stack: the default answer for B2B sites

A system font stack uses fonts already installed on the user’s device, so there are zero network requests and text shows immediately. B2B stores you run yourself are mostly spec tables, case descriptions, and technical articles, so the typography doesn’t need decorative or custom brand fonts — a system font stack looks perfectly good and stays consistent across platforms.

A CSS stack you can drop in as-is: font-family: -apple-system, BlinkMacSystemFont, “Segoe UI”, Roboto, “Helvetica Neue”, Arial, “PingFang SC”, “Microsoft YaHei”, sans-serif. It covers macOS, Windows, Android and iOS, with a system font for both Chinese and English. Counterintuitive take:Using system fonts won’t make your site look “unprofessional”. In fact, design benchmarks like GitHub, Stripe and Linear all use system font stacks. For how to pick a WP theme, see WordPress tech stack.

Solution Above-the-fold text Extra downloads Best for
System fonts only Shows instantly 0KB Most B2B standalone sites
1 web font for headings Swapped in after fallback 30 to 80KB Sites that want a strong brand feel
2 web font families Swapped in after fallback 80 to 150KB Design-led brand sites
3+ web fonts Noticeable FOUT/FOIT >200KB Not recommended
Full Chinese web font FOIT for several seconds 2 to 8MB Barely viable

3. font-display: swap and FOIT: don’t hide your text

font-display is an @font-face property that controls how text renders while the font loads. Four values: auto (browser default, used to be block), block (hidden briefly, then falls back after 3 seconds), swap (show the fallback immediately, swap in the font once it loads), and optional (the browser decides, and tends not to download the font at all).

B2B standalone sites should always use swap. On a 3G connection, block means a blank above-the-fold area for 3 seconds, and visitors are long gone by then. optional suits fonts that aren’t critical to the brand, but it’s unpredictable. A common misconception is that “swap causes font flicker (FOUT)”, but for a B2B site, letting users read your specs and contact details matters far more than waiting on a fancy heading. A font flickering is perfectly acceptable; text nobody can see is the real loss. For CSS/JS loading strategy, see CSS/JS optimization.

font-display Blocking period After the font loads Use cases
auto/block Text invisible for up to 3 seconds Swapped to the web font Not recommended
swap Fallback shows immediately Swapped once the font loads Default for B2B sites
fallback Hidden for 100ms, then fallback Swapped only if loaded within 3 seconds Semi-critical fonts
optional Hidden for 100ms, then fallback Not swapped on this visit Purely decorative fonts

4. preload and subsetting: make the fonts that should be fast, fast

If your above-the-fold heading has to use a web font, use preload to tell the browser to download it early at high priority: add <link rel=”preload” href=”font.woff2″ as=”font” type=”font/woff2″ crossorigin> in the head. The font then starts downloading before the CSS is parsed, which can pull LCP forward by 200 to 400 milliseconds. Note that you can’t omit the crossorigin attribute, or preload won’t work.

Subsetting is the other optimization you have to do. For English fonts, keep only Latin letters, digits, and common symbols, which cuts the file from 100KB to 30KB. Chinese fonts have such large character sets that even after subsetting they run 200KB to 1MB, which is why Chinese-language sites are better off using system fonts. Use glyphhanger or the text parameter in Google Fonts to load only the characters you actually use. One real example: an above-the-fold headline uses just 20 Chinese characters, so you subset only those 20, and the file drops from 4MB to 5KB. For CDN caching, see CDN comparison.

Font optimization action Size / latency gain Implementation difficulty Recommendation
Use woff2 only 30% smaller than ttf Low Must do
Load only weights 400 and 700 60% less download Low Must do
Subset the English character set Another 50% off Medium Recommended
Preload above-the-fold fonts LCP 200 to 400ms earlier Low Above-the-fold headline uses
Full Chinese web font Negative High Don’t do it

5. Validation: check the font loading waterfall in DevTools

How do you validate your font optimization? Open the Network panel in Chrome DevTools, filter by Font, and check three things: the number of font files (should be no more than 2 families, no more than 2 weights per family), the size of each font file (under 50KB is healthy), and when each font starts downloading (with preload it should be among the earliest requests, not discovered after CSS parsing). If any font file is over 100KB, it’s worth going back to check whether the weights or subsets are misconfigured.

Then use Lighthouse to check the “Avoid layout shifts from web fonts” and “Preload key requests” audits. An actionable checklist: font-display is swap everywhere, above-the-fold fonts are preloaded, woff2 format, no legacy ttf/eot formats, and Google Fonts loaded with the display=swap parameter. For how to handle fonts on multilingual sites, see Charts and tables on multilingual sites. For site monitoring, see Site monitoring alerts.

Validation item Pass criteria Measurement tool
font-display All swap or fallback CSS review
Above-the-fold fonts preloaded Above-the-fold headline font is preloaded DevTools Network
Font file format woff2 only Filter Network by Font
Single font file size ≤50KB Network Size column
Number of font families ≤2 CSS review
LCP 75th percentile ≤2.5s CrUX / RUM

FAQ

Do B2B sites really need web fonts?
No. A system font stack is the right answer for most B2B sites. B2B content is mostly specs, case studies, and technical articles, and system fonts look perfectly fine for that, plus they render instantly with zero network requests. GitHub, Stripe, and Linear all use system font stacks. Only load a web font family when brand feel matters a lot and the above-the-fold headline needs a custom typeface, and then it must be swap + subset + preload.
What’s the difference between font-display:swap and block?
block hides text while the font loads (FOIT) and only falls back after 3 seconds; swap shows text immediately in the fallback font and swaps it once the web font finishes downloading. B2B sites must use swap, because letting users read the content matters more than waiting for a fancy headline. On a 3G connection, block means a blank above-the-fold area for 3 seconds, and visitors are long gone. fallback is the middle ground: hide for 100ms, then use the fallback, and swap only if the font loads within 3 seconds.
Can I use a web font for Chinese text?
Almost never worth it. A Chinese font file runs 2-8 MB, and even after subsetting it’s still 200 KB-1 MB, way over your first-screen budget. And even with subsetting, users hit characters outside the subset when they page through and have to reload. The right approach is a system font stack: PingFang SC on macOS, Microsoft YaHei on Windows, Noto Sans CJK on Android. Consistent across platforms, zero downloads. Use an image only for the brand logo; everything else stays in system fonts.
Why does preloading a font need crossorigin?
Because font files are CORS requests. Even same-origin, you must include the crossorigin attribute, or the preload downloads the file but @font-face never uses it, wasting the request. The correct form is link rel=preload as=font type=font/woff2 href=… crossorigin. A missing crossorigin is the most common reason preload fails, and Lighthouse will flag it as “preloaded resource not used.”
What’s the fastest way to load Google Fonts?
Three steps. First, add the display=swap parameter to the URL. Second, request only the 400 and 700 weights, not the full set. Third, self-host the font files instead of pointing at fonts.googleapis.com. Google Fonts is blocked or slow in mainland China and on some corporate networks in Europe and the US; self-hosting puts them on your own CDN and saves a DNS handshake. Use Google Webfonts Helper to download the woff2 files locally.
How do I verify font optimization is done?
In DevTools Network, filter by Font and check three things: no more than 2 font families, each file 50 KB or less, and first-screen fonts among the earliest requests. In CSS, confirm every font-display is swap and there are no legacy ttf/eot formats. In Lighthouse, check that the “Avoid layout shifts” and “Preload key requests” audits are both green. Finally, use CrUX to check whether the 75th percentile LCP is 2.5 seconds or less. If LCP is still slow, open the Performance panel to see whether fonts are blocking.

6. FOUT and layout shift: how to stop the page from jumping when fonts swap

With the swap strategy, when the fallback font is replaced by the web font, the different character widths and heights can make the page jump. That’s where CLS (Cumulative Layout Shift) comes from. Stripe measured it: a single font swap causes 0.05 to 0.1 CLS on average, right on the edge of the 0.1 passing threshold. On B2B sites, long body copy and many paragraphs mean the line-break differences from the swap make images jump up and down.

The fix is the font-size-adjust and size-adjust descriptors. In @font-face, use size-adjust: 105% and ascent-override to bring the web font’s metrics close to the fallback’s, so the swap barely shifts anything. An easier route is to pick a fallback whose metrics are close to the web font’s, for example Arial as the fallback for Inter, or just use a system font stack and skip the swap entirely. One concrete check: run Lighthouse’s “Avoid layout shifts from web fonts” audit. If the score is low, first see whether swap is the cause, then decide whether to add size-adjust.

Handling FOUT Fix CLS Implementation difficulty
Leave it, accept the flicker 0.05 to 0.1 None
Fine-tune metrics with size-adjust Get it below 0.02 Medium, needs testing
Pick a fallback with close metrics Get it below 0.03 Low
Just drop web fonts 0 Zero cost

Final thoughts

The core call on fonts is simple: people come to a B2B site to read specs, see case studies, and find a contact, not to admire typography. A system font stack is the right answer 95% of the time, and the few hundred KB and few hundred milliseconds you save matter far more than the visual difference. Even if you keep web fonts, hold to four rules: two families, two weights, woff2 format, swap loading.

Three steps to ship: first, switch your theme fonts to a system font stack this week and delete the extra Google Fonts references. Second, add font-display:swap to every web font you keep, cut to just 400 and 700, and self-host the woff2 files. Third, preload the above-the-fold heading font and add crossorigin. Check in two weeks whether LCP and your font-related audits have improved.

One line worth remembering: nobody stays a second longer because your site uses Inter, but they will close the tab if they wait 3 seconds for a font to load. Fonts are a detail, but details decide whether you win those first 2.5 seconds.

Crater
Crater Crater experts

Official Organization · Crater

Crater is a go-global services company specializing in WordPress standalone site development + Google SEO + GEO full-funnel growth, serving sectors including smart hardware, industrial equipment, SaaS, and new energy.

Product CapabilitiesMethodology WhitepaperIndustry Report
965articles shared
78knowledge base articles