CSS 字体栈:回退字体最佳实践
CSS Font Stacks: Best Practices for Fallback Fonts
A CSS font stack is the ordered list of typefaces specified in a font-family declaration. The browser works through the list from left to right, using the first font it can find and render. If none of the named fonts are available, it falls back to the final value — always a generic family name like sans-serif, serif, or monospace.
Writing good font stacks is more than listing a few font names. It's about understanding platform differences, how fallback fonts affect layout, how to use modern CSS properties to match fallback metrics, and which recipes to reach for in common design scenarios.
How CSS Font Stacks Work
When a browser encounters font-family: 'Inter', 'Helvetica Neue', Arial, sans-serif, it checks:
- Is 'Inter' available? (Downloaded via
@font-face, or pre-installed) — Use it - If not: Is 'Helvetica Neue' installed? — Use it
- If not: Is 'Arial' installed? — Use it
- If not: Use the OS default sans-serif font
The browser evaluates this per character, not per element. If a character in your text doesn't exist in Inter's glyph set (say, a Chinese character), the browser falls back to the next font in the stack that contains that glyph. This makes font stacks a partial per-character fallback system as well.
What "Available" Means
A font is available if:
- It was declared with @font-face and the file has been downloaded
- It was referenced via local() in an @font-face and is installed on the system
- It is a built-in system font
/* This declares Inter as available via download */
@font-face {
font-family: 'Inter';
src: url('/fonts/Inter-Variable.woff2') format('woff2');
font-weight: 100 900;
font-display: swap;
}
/* Now this stack uses the downloaded Inter */
body {
font-family: 'Inter', system-ui, sans-serif;
}
Generic Families
Always end your stack with a generic family. Never leave a font-family declaration without one.
/* Generic families */
.sans { font-family: system-ui, sans-serif; }
.serif { font-family: Georgia, serif; }
.mono { font-family: 'Courier New', monospace; }
.cursive { font-family: cursive; }
.fantasy { font-family: fantasy; }
/* The newest: system-ui, ui-sans-serif, ui-serif, ui-monospace */
.native-ui { font-family: system-ui, -apple-system, sans-serif; }
The System Font Stack (system-ui)
The system font stack tells the browser to use the operating system's default UI font. This produces text that feels native to the user's platform — the same font they see in system menus, dialogs, and native apps.
The Minimal System Stack
body {
font-family: system-ui, -apple-system, sans-serif;
}
system-ui is the CSS specification keyword for the system UI font. Browser support is excellent across all modern platforms. -apple-system is a legacy alias for older Safari. The final sans-serif is the generic fallback.
What This Resolves To
| Platform | Font |
|---|---|
| macOS / iOS | SF Pro / San Francisco |
| Windows 11+ | Segoe UI Variable |
| Windows 10 | Segoe UI |
| Android | Roboto |
| ChromeOS | Roboto |
| Linux | Depends on distribution — often Cantarell, Ubuntu, or Noto Sans |
The Extended System Stack
Some teams use a longer stack for maximum coverage of older systems:
body {
font-family:
system-ui,
-apple-system, /* Safari on macOS/iOS < 2019 */
BlinkMacSystemFont, /* Chrome on macOS < 2019 */
'Segoe UI', /* Windows */
Roboto, /* Android */
Oxygen, /* KDE Linux */
Ubuntu, /* Ubuntu Linux */
Cantarell, /* GNOME Linux */
'Helvetica Neue', /* macOS/iOS fallback */
Arial, /* Universal fallback */
sans-serif; /* Generic */
}
This extended stack is what GitHub famously used before simplifying to system-ui. For most modern projects, the shorter version is sufficient.
When to Use the System Stack
Use system-ui when:
- Building developer tools, dashboards, or admin interfaces
- Performance is the top priority and custom fonts aren't worth the overhead
- You want a native app aesthetic
- The UI is a means to an end, not a brand expression
Use a custom font stack when: - Brand typography is important - The reading experience requires a specific typeface (long-form content) - You need consistent cross-platform appearance
Building Custom Stacks: Web Font + Fallbacks
When you use a custom web font, you need fallbacks that activate while the font loads and for users where the font fails to load.
Matching Fallback to the Web Font
The ideal fallback resembles your web font in category, proportion, and weight. A good fallback minimizes the visual jump when the web font swaps in.
/* Inter → system-ui → Helvetica Neue → Arial */
body {
font-family: 'Inter', system-ui, -apple-system, 'Helvetica Neue', Arial, sans-serif;
}
/* Source Serif 4 → Georgia → Times New Roman */
.article-body {
font-family: 'Source Serif 4', Georgia, 'Times New Roman', serif;
}
/* Lora → Palatino (wider, old-style serif) */
blockquote {
font-family: 'Lora', Palatino, 'Book Antiqua', Georgia, serif;
}
/* Playfair Display → Didot (similar high-contrast display serif) */
.display-heading {
font-family: 'Playfair Display', Didot, 'Palatino Linotype', serif;
}
/* JetBrains Mono → Menlo → Consolas → monospace */
code {
font-family: 'JetBrains Mono', Menlo, Consolas, 'Courier New', monospace;
}
Platform-Aware Fallback Chaining
Different platforms have different pre-installed fonts. A well-crafted stack handles each:
/* Heading stack: Playfair Display → platform serif alternates */
h1, h2, h3 {
font-family:
'Playfair Display', /* Web font */
'Palatino Linotype', /* Windows */
Palatino, /* macOS */
'Book Antiqua', /* Older Windows */
Georgia, /* Universal serif fallback */
serif;
}
/* Sans-serif body: Roboto → platform sans */
body {
font-family:
'Roboto', /* Web font */
'Helvetica Neue', /* macOS + iOS older */
Helvetica, /* macOS + legacy */
Arial, /* Windows + universal */
sans-serif;
}
size-adjust: Matching Fallback Metrics
The size-adjust descriptor in @font-face is a game-changer for reducing layout shift when fonts swap. It lets you scale a fallback font to approximately match the dimensions of the web font.
When fonts have different x-heights, cap heights, and character widths, swapping from fallback to web font causes content to reflow — paragraphs shift, buttons resize, headings reflux. size-adjust lets you compensate by scaling the fallback.
How size-adjust Works
/* Create a modified version of Arial that matches Inter's metrics */
@font-face {
font-family: 'Inter Fallback';
src: local('Arial'); /* Use Arial from the system */
size-adjust: 107%; /* Scale up to match Inter's width */
ascent-override: 90%; /* Match Inter's ascent */
descent-override: 22%; /* Match Inter's descent */
line-gap-override: 0%; /* Match Inter's line gap */
}
/* Web font */
@font-face {
font-family: 'Inter';
src: url('/fonts/Inter-Variable.woff2') format('woff2');
font-weight: 100 900;
font-display: swap;
}
/* Stack: Inter → adjusted Arial fallback → system sans */
body {
font-family: 'Inter', 'Inter Fallback', system-ui, sans-serif;
}
When the page first loads, Inter Fallback (the adjusted Arial) renders. When Inter finishes loading and swaps in, the layout change is minimal because the metrics have been compensated.
Finding the Right Values
The exact values depend on the specific fonts being compared. You can find them by:
- Computed experimentally: Render the same text in both fonts at the same size and measure the difference
- Using a tool: The Font Style Matcher and Malte Ubl's Automatic Font Matching calculate these values
/* Roboto → Roboto fallback using adjusted Arial */
@font-face {
font-family: 'Roboto Fallback';
src: local('Arial');
size-adjust: 100%;
ascent-override: 92.7189%;
descent-override: 24.4099%;
line-gap-override: 0%;
}
/* Lora → Lora fallback using adjusted Georgia */
@font-face {
font-family: 'Lora Fallback';
src: local('Georgia');
size-adjust: 104%;
ascent-override: 87%;
descent-override: 23%;
line-gap-override: 0%;
}
The Resulting Font Stacks
body {
font-family: 'Roboto', 'Roboto Fallback', sans-serif;
}
.serif-body {
font-family: 'Lora', 'Lora Fallback', serif;
}
This approach produces near-zero Cumulative Layout Shift (CLS) from font swapping — a Core Web Vital metric that Google uses for search ranking. If your CLS is impacted by font loading, size-adjust is your most effective tool.
Platform-Specific Considerations
macOS and iOS
macOS ships with a small set of pre-installed fonts: - Sans-serif: Helvetica Neue, Helvetica, Arial, system-ui (San Francisco) - Serif: Georgia, Times New Roman, Palatino - Mono: Menlo, Courier New - Notable extras: Didot, Optima, Gill Sans, Futura (on macOS)
macOS has long had subpixel antialiasing for fonts. Starting with macOS Mojave, Apple disabled it across the OS. -webkit-font-smoothing: antialiased can still affect rendering — see the font smoothing guide for details.
Windows
Windows 11 ships with a richer set of fonts than previous versions: - Sans-serif: Segoe UI Variable, Arial, Calibri, Tahoma, Verdana, Trebuchet MS - Serif: Georgia, Times New Roman, Cambria, Palatino Linotype - Mono: Consolas, Courier New, Cascadia Code (in newer builds)
Verdana and Georgia were designed specifically for screen readability and have unusually wide character widths — keep this in mind when using them as fallbacks.
Android
Android ships with Roboto as its primary system font across the standard interface. Noto (for international character coverage) is also bundled. Most Android browsers ship their own font fallback behavior.
/* A robust cross-platform body stack */
body {
font-family:
'Inter',
'Inter Fallback', /* Adjusted fallback */
system-ui,
-apple-system,
'Segoe UI',
Roboto,
Arial,
sans-serif;
}
Common Font Stack Recipes
Recipe 1: Clean Modern Sans (Tech / Startup)
:root {
--font-sans: 'Inter', system-ui, -apple-system, 'Segoe UI', Roboto, 'Helvetica Neue', Arial, sans-serif;
}
Inter or system UI fallback. Clean, readable, neutral. Works at every size.
Recipe 2: Humanist Sans (Agency / Creative)
:root {
--font-sans: 'Source Sans 3', 'Gill Sans', 'Gill Sans MT', Calibri, 'Trebuchet MS', sans-serif;
}
Warm humanist feel. Gill Sans as a fallback (widely installed on macOS).
Recipe 3: Geometric Display (Brand Headings)
:root {
--font-display: 'Raleway', 'Futura', 'Century Gothic', 'Trebuchet MS', sans-serif;
}
Futura is pre-installed on macOS. Good for geometric display headings.
Recipe 4: Scholarly Serif (Editorial / Blog)
:root {
--font-serif: 'Lora', 'Palatino Linotype', Palatino, Georgia, 'Times New Roman', serif;
}
Palatino as a platform fallback gives a refined, humanist feel. Georgia as the universal backup.
Recipe 5: Monospace Code
:root {
--font-mono: 'JetBrains Mono', 'Fira Code', 'Cascadia Code', Menlo, Consolas, 'Courier New', monospace;
}
JetBrains Mono and Fira Code for ligature support. Menlo on macOS, Consolas on Windows, Courier New as the universal fallback.
Recipe 6: Pure System UI (Performance First)
:root {
--font-system: ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont,
'Segoe UI', Roboto, 'Helvetica Neue', Arial, 'Noto Sans',
sans-serif, 'Apple Color Emoji', 'Segoe UI Emoji',
'Segoe UI Symbol', 'Noto Color Emoji';
}
This is Tailwind CSS's default font stack — highly compatible, zero downloads, native feel. Emoji-capable via the trailing emoji font entries.
Recipe 7: Transitional Serif (Finance / Legal)
:root {
--font-serif: 'Source Serif 4', 'Cambria', Georgia, 'Times New Roman', Times, serif;
}
Source Serif 4 has excellent OpenType features and optical sizing. Cambria is a well-designed screen serif on Windows.
/* Apply recipes across component types */
body {
font-family: var(--font-sans);
}
.prose, article, .blog-post {
font-family: var(--font-serif);
}
code, pre, kbd {
font-family: var(--font-mono);
}
.hero-headline {
font-family: var(--font-display);
}
A well-constructed font stack is an act of defensive design. Your web font is the intention; your fallbacks are the guarantee that the experience remains good for every user, on every device, at every connection speed.
排版术语
试试这些工具
提及的字体
Roboto 由 Christian Robertson 为 Google 的 Material Design 体系设计,是网页和 Android 平台上使用最广泛的字体。其双重特质在机械精度与自然阅读节奏之间取得平衡,无论用于 UI 标签还是长篇文本都同样出色。这款可变字体支持宽度和字重轴,并覆盖西里尔文、希腊文及扩展拉丁字符。
Steve Matteson 设计的这款人文主义无衬线字体笔画垂直、字腔开阔,优先保证在不同屏幕尺寸和分辨率下的可读性。作为有史以来部署最广泛的网络字体之一,它呈现出中性专业的调性,非常适合正文文字、邮件模板与网页应用。可变的宽度与字重轴,加上希伯来文和希腊文支持,使其成为多用途的多语言主力字体。
Rasmus Andersson 花费数年时间专门为电脑屏幕打磨这款新怪诞体,优化了字间距、x 高度与笔画对比,以确保在数字显示器的小尺寸下也拥有高可读性。光学尺寸轴(opsz)让字体能够在标注文字与标题之间自动调整设计,而字重轴则覆盖从纤细到极粗的完整范围。它已成为全球仪表盘、文档站点与开发者工具的事实标准选择。