Every web font has a loading window — the gap between the page rendering and the font file arriving. During this window, the browser uses your fallback font. If the fallback is visually different (different size, different line height, different character width), text reflows when the web font swaps in. That reflow is Cumulative Layout Shift (CLS), and it degrades both user experience and Core Web Vitals scores.

The goal isn't just "pick a fallback." It's engineering a fallback that occupies the same physical space as the web font, so the swap is seamless. This guide covers the system font stack, metric matching with CSS overrides, and the edge cases that trip up most implementations — monospace, emoji, CJK, and Unicode coverage gaps.

The Modern System Font Stack

System fonts are pre-installed on the user's device and render instantly — zero network requests, zero loading delay. Every fallback chain should end with system fonts.

Sans-Serif Stack

font-family: 'Your Web Font', system-ui, -apple-system, 'Segoe UI', 
  Roboto, 'Noto Sans', Ubuntu, Cantarell, 'Helvetica Neue', sans-serif;
ValuePlatformFont
system-uimacOS/iOSSan Francisco (SF Pro)
system-uiWindowsSegoe UI
system-uiAndroidRoboto
-apple-systemOlder SafariSan Francisco (pre-system-ui support)
'Segoe UI'WindowsDirect fallback for older browsers
RobotoAndroid/ChromeOSDirect fallback
'Noto Sans'LinuxCommon pre-installed font
UbuntuUbuntu LinuxDefault UI font
CantarellGNOME/FedoraDefault UI font
'Helvetica Neue'macOS (older)Pre-San Francisco default
sans-serifAnyGeneric fallback

system-ui resolves to the platform's default UI font, covering macOS, Windows, and Android in one keyword. The named fonts after it are fallbacks for browsers that don't support system-ui (increasingly rare in 2026) and for Linux, which has fragmented font installation.

Serif Stack

font-family: 'Your Serif Font', 'Iowan Old Style', 'Palatino Linotype', 
  Palatino, Georgia, 'Noto Serif', serif;

Serif system fonts are less standardized across platforms. Georgia is the universal safe bet — installed on macOS, Windows, and most Linux distributions. Palatino/Palatino Linotype provides a more elegant fallback. 'Iowan Old Style' is available on Apple devices and is one of the best-looking pre-installed serif fonts.

Monospace Stack

font-family: 'Your Mono Font', ui-monospace, 'SF Mono', 'Cascadia Code', 
  'Consolas', 'Liberation Mono', 'DejaVu Sans Mono', Menlo, Monaco, monospace;

ui-monospace resolves to the platform's default monospace UI font (SF Mono on macOS, Cascadia Code on modern Windows). The named fonts cover specific platforms: Consolas for Windows, Menlo/Monaco for older macOS, Liberation Mono and DejaVu Sans Mono for Linux.

Monospace fallbacks are critical for code blocks and preformatted text. If the fallback isn't monospace, code alignment breaks completely — columns don't line up, indentation is inconsistent, and ASCII art becomes unreadable.

Metric Matching: Zero-CLS Font Swaps

The biggest font performance problem in 2026 isn't load time — it's the layout shift when the web font replaces the fallback. Even with font-display: swap and preloading, the swap causes CLS if the two fonts have different metrics.

Why Font Metrics Cause Layout Shift

Two fonts with the same font-size: 16px produce different physical dimensions because fonts define their own internal metrics:

  • x-height: The height of lowercase letters (like 'x'). A taller x-height makes text appear larger
  • Ascender height: How far tall letters (like 'd', 'h') extend above the x-height
  • Descender depth: How far letters like 'g', 'p', 'y' extend below the baseline
  • Character width (advance width): How wide each character is, determining how many characters fit per line
  • Line gap: Extra space the font requests between lines, on top of ascender + descender

When Inter (x-height: 0.52 of font-size) replaces Arial (x-height: 0.52 of font-size), the swap is almost invisible because their metrics are close. When a display font with narrow characters replaces Arial, every line break changes and paragraphs gain or lose lines — visible, jarring CLS.

CSS Font Metric Override Properties

Four CSS properties let you adjust the fallback font's metrics to match the web font:

/* Define a custom fallback with adjusted metrics */
@font-face {
  font-family: 'Inter Fallback';
  src: local('Arial');
  size-adjust: 107.64%;
  ascent-override: 89.71%;
  descent-override: 22.43%;
  line-gap-override: 0%;
}

/* Use both in the font stack */
body {
  font-family: 'Inter', 'Inter Fallback', system-ui, sans-serif;
}

What each property does:

  • size-adjust — Scales the fallback font's glyphs by a percentage. If the fallback has narrower characters than the web font, increase this value so they occupy the same width
  • ascent-override — Sets the ascender height as a percentage of font-size. Controls where the top of the text box sits
  • descent-override — Sets the descender depth. Controls where the bottom of the text box sits
  • line-gap-override — Sets extra space between lines. Most web fonts use 0; set fallback to 0 to match

When all four values are tuned correctly, the fallback font occupies the exact same space as the web font. Lines break at the same points, paragraphs have the same height, and the swap produces zero layout shift.

Calculating Override Values

Three approaches, from easiest to most precise:

  1. Fontaine (npm): Generates @font-face CSS with metric overrides automatically. npx fontaine scans your font files and outputs the CSS
  2. Next.js next/font: Calculates overrides automatically when you use next/font/google or next/font/local. Zero configuration
  3. Manual calculation: Read the OS/2 table from both fonts (fonttools or opentype.js), then calculate:
    size-adjust = web_avg_char_width / fallback_avg_char_width * 100%
    ascent-override = web_ascent / (web_ascent + web_descent + web_line_gap) * 100%
    descent-override = web_descent / (web_ascent + web_descent + web_line_gap) * 100%
    line-gap-override = web_line_gap / (web_ascent + web_descent + web_line_gap) * 100%

Override Values for Popular Fonts

Web FontFallbacksize-adjustascent-overridedescent-overrideline-gap-override
InterArial107.64%89.71%22.43%0%
RobotoArial100.3%92.49%24.34%0%
Open SansArial105.27%101.18%27.25%0%
LatoArial97.38%102.78%22.22%0%
MontserratArial103.6%84.05%20.69%0%
Source Code ProConsolas92.8%98.37%27.4%0%

These values produce near-zero CLS for the most common Google Fonts when Arial (sans-serif) or Consolas (monospace) is the system fallback. Adjustments may be needed for different fallback fonts.

X-Height Matching

If CSS metric overrides aren't an option (older browser support, email templates), choose a fallback font whose x-height is closest to your web font's x-height. X-height is the single most important metric for visual similarity — it determines how "big" text appears at a given font-size.

General matching guidelines:

  • Inter, Roboto, Open Sans: Arial is a close x-height match. All have high x-heights (~0.52 ratio)
  • Montserrat, Poppins: Verdana is a closer match than Arial (wider characters, similar x-height)
  • Georgia: Times New Roman is the closest pre-installed serif match
  • Lato: Helvetica Neue on macOS, Arial on Windows

The ex CSS unit is the x-height of the current font. You can use it for debugging: set a 1ex-tall box in both the web font and fallback font and compare visually.

Emoji Fallbacks

Emoji rendering uses platform-specific color fonts that are separate from your text font. The fallback chain for emoji is handled by the operating system, not your CSS font-family declaration. But there are cases where you want explicit control.

Emoji Font Stack

/* Explicit emoji fallback (rarely needed) */
.emoji {
  font-family: 'Apple Color Emoji', 'Segoe UI Emoji', 'Noto Color Emoji', 
    'Twemoji Mozilla', sans-serif;
}
FontPlatformStyle
Apple Color EmojimacOS, iOSApple's emoji design
Segoe UI EmojiWindowsMicrosoft's emoji design
Noto Color EmojiAndroid, LinuxGoogle's emoji design
Twemoji MozillaFirefox (bundled)Twitter's emoji design

Most of the time, you don't need an explicit emoji font stack. Browsers automatically fall back to the platform's emoji font when they encounter emoji characters. The explicit stack is useful for ensuring consistent emoji rendering in specific UI elements or when using emoji as icons.

Text vs Emoji Presentation

Some Unicode characters have both text and emoji presentations. The "information" symbol (i), the "warning" triangle, and various arrows exist as both plain text glyphs and color emoji. The Variation Selector controls which version renders:

  • U+FE0E (VS15) forces text presentation — monochrome glyph from the text font
  • U+FE0F (VS16) forces emoji presentation — color glyph from the emoji font

If your text font renders certain symbols as emoji unexpectedly, append VS15 to force the text version. Conversely, if emoji render as text (common on some Linux configurations), append VS16.

Unicode Coverage Gaps

Every font covers a specific set of Unicode code points. When text contains characters outside the font's coverage, the browser falls back to the next font in the stack. If no font in the stack covers the character, the user sees a tofu — the empty rectangle (□) that indicates a missing glyph.

Preventing Tofu

  • Include Noto Sans in your fallback chain: Google's Noto font family ("No Tofu") aims to cover every Unicode character. 'Noto Sans' is pre-installed on Android and many Linux distributions, and covers Latin, Greek, Cyrillic, Arabic, Hebrew, Devanagari, and more
  • Use unicode-range @font-face: Declare separate @font-face rules for different scripts, each pointing to a font that covers that range. The browser loads only the fonts needed for the characters on the page
  • Test with diverse content: If your site accepts user-generated content, test with Arabic, CJK, and emoji characters to verify fallback rendering

CJK Fallback Chain

/* For sites that may display CJK characters */
font-family: 'Your Font', system-ui, -apple-system, 'Segoe UI', 
  Roboto, 'Noto Sans', 'Noto Sans CJK SC', 'Noto Sans CJK JP', 
  'Microsoft YaHei', 'PingFang SC', 'Hiragino Sans', sans-serif;

'Noto Sans CJK SC' covers Simplified Chinese, 'Noto Sans CJK JP' covers Japanese, 'Microsoft YaHei' is the default CJK font on Windows, and 'PingFang SC' and 'Hiragino Sans' cover macOS/iOS. Including all of these ensures CJK characters render correctly regardless of platform.

A bulletproof fallback strategy has three layers: (1) a system font stack that covers every platform, (2) CSS metric overrides that match the fallback's metrics to the web font, and (3) extended Unicode coverage for edge-case characters. The metric matching layer is the most impactful — it turns a jarring font swap into an almost invisible transition.

Pair this with font-display: swap, preloading, and WOFF2 compression (convert your fonts to WOFF2) for a complete web font strategy. Users see text instantly in a visually similar fallback, the web font loads in the background, and the swap happens with zero layout shift.