CSS Font Stacks: Best Practices for Fallback Fonts
Embed This Widget
Add the script tag and a data attribute to embed this widget.
Embed via iframe for maximum compatibility.
<iframe src="https://fontfyi.com/iframe/entity//" width="420" height="400" frameborder="0" style="border:0;border-radius:10px;max-width:100%" loading="lazy"></iframe>
Paste this URL in WordPress, Medium, or any oEmbed-compatible platform.
https://fontfyi.com/entity//
Add a dynamic SVG badge to your README or docs.
[](https://fontfyi.com/entity//)
Use the native HTML custom element.
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.
Termes typographiques
Essayez ces outils
Polices mentionnées
Conçue par Christian Robertson pour l'écosystème Material Design de Google, cette police sans-serif néo-grotesque est la plus utilisée sur le web et sur Android. Sa conception à double nature équilibre précision mécanique et rythme de lecture naturel, s'adaptant aussi bien aux libellés d'interface qu'aux longs textes. La police variable prend en charge les axes de chasse et de graisse, ainsi que les écritures cyrillique, grecque et latine étendue.
Steve Matteson a conçu cette police sans-serif humaniste avec un axe droit et des ouvertures généreuses qui privilégient la lisibilité sur toutes les tailles d'écran et résolutions. L'une des polices web les plus déployées jamais publiées, elle adopte un ton neutre et professionnel idéal pour le texte courant, les modèles d'e-mail et les applications web. Les axes variables de chasse et de graisse, plus le support hébreu et grec, en font une option multilingue très polyvalente.
Rasmus Andersson a passé des années à affiner ce néo-grotesque spécifiquement pour les écrans d'ordinateur, en optimisant l'espacement des lettres, la hauteur d'x et le contraste des traits pour une haute lisibilité aux petites tailles sur les affichages numériques. Un axe de taille optique (opsz) permet à la police d'adapter automatiquement son design aux légendes ou aux titres, tandis que l'axe de graisse couvre toute la gamme du fin au noir. Il est devenu le choix de référence pour les tableaux de bord, les sites de documentation et les outils pour développeurs dans le monde entier.