Font-display swap configured on all web fonts
Why it matters
Without font-display, the browser's default behavior is to block text rendering for up to 3 seconds while waiting for the web font to download — Flash of Invisible Text (FOIT). On slow connections, users see blank text boxes where your content should be (ISO-25010 time-behaviour). Even on fast connections, FOIT contributes to CLS when the font finally loads and changes character widths, causing visible layout shift. Every web font without font-display: swap is a potential 3-second invisible-text window.
Severity rationale
Medium because missing `font-display` causes up to 3 seconds of invisible text on slow connections and triggers CLS-contributing font swaps when the web font finally loads.
Remediation
Add font-display: swap to every @font-face declaration so the browser shows fallback text immediately and swaps in the web font when ready:
/* src/styles/fonts.css */
@font-face {
font-family: 'CustomFont';
src: url('/fonts/custom-regular.woff2') format('woff2');
font-weight: 400;
font-style: normal;
font-display: swap;
}
With next/font/google, font-display: swap is applied automatically:
// src/app/layout.tsx
import { Inter } from 'next/font/google'
const inter = Inter({ subsets: ['latin'] }) // font-display: swap included
With Google Fonts via <link>, append &display=swap to the URL:
<link href="https://fonts.googleapis.com/css2?family=Inter&display=swap" rel="stylesheet">
Set a size-similar fallback font stack (e.g., Arial for Inter) to minimize the visual jump on swap.
Detection
-
ID:
font-display -
Severity:
medium -
What to look for: Count all relevant instances and enumerate each. Check
@font-facedeclarations forfont-displayproperty. Look in CSS files, font loading libraries (next/font, Google Fonts, Typekit), or<link>tags for web fonts. Verify all font faces havefont-displayset toswap,optional, or other non-blocking value. -
Pass criteria: All
@font-facedeclarations includefont-display: swap(or another non-blocking value). At least 1 implementation must be verified. Fonts are loaded asynchronously, and fallback fonts display immediately. -
Fail criteria:
font-displayis missing (defaults toauto, blocking on some browsers), or set toblock(FOIT). Fonts block text rendering for 3+ seconds. -
Skip (N/A) when: The project uses only system fonts or no web fonts.
-
Detail on fail: Specify the font loading strategy. Example:
"Five @font-face rules with no font-display; text blocks for 2.5s while font loads (FOIT). No fallback font specified"or"font-display: block on all fonts; poor UX on slow networks". -
Remediation:
font-display: swapshows fallback text immediately and swaps in the web font when ready:@font-face { font-family: 'CustomFont'; src: url('font.woff2') format('woff2'); font-display: swap; /* show fallback immediately, swap in web font */ }With next/font:
import { Inter } from 'next/font/google' const inter = Inter() // already optimized with font-display: swapWith Google Fonts link:
<link rel="preconnect" href="https://fonts.googleapis.com"> <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin> <link href="https://fonts.googleapis.com/css2?family=Inter&display=swap" rel="stylesheet">
External references
- iso-25010:2011 · performance-efficiency.time-behaviour — Time Behaviour
Taxons
History
- 2026-04-18·v1.0.0·Initial import from performance-core·automated