# Ensure text remains visible during webfont load: the font-display fix

**Audit ID:** `font-display` · **Category:** Performance

<!--QA-->
> **Quick answer:** This audit fails when a web font loads without a font-display strategy, letting browsers hide text while the font downloads. Fix it by adding font-display: swap to every @font-face rule, or the display=swap parameter to Google Fonts URLs. Use optional for non-branding fonts to avoid layout shift entirely.
<!--/QA-->

This audit fails when a web font is loaded without a `font-display` strategy, which lets browsers hide your text (sometimes for several seconds) while the font file downloads. The fix is one CSS descriptor, but choosing the right value matters.

## TL;DR

- **What:** Every `@font-face` rule without a `font-display` descriptor risks invisible text during load (FOIT: flash of invisible text).
- **Fix:** Add `font-display: swap` to self-hosted fonts, or `&display=swap` to Google Fonts URLs.
- **Nuance:** `swap` trades invisible text for a font swap that can shift layout. `optional` is the strictest choice for performance.

## What does "ensure text remains visible during webfont load" mean?

Lighthouse checks every `@font-face` rule the page uses. If a rule has no `font-display` descriptor (or uses `block`), the browser is allowed to render the text as invisible while the font downloads: up to 3 seconds in most browsers, indefinitely in some older ones. On a slow connection your visitors stare at a page with headings and paragraphs that are physically there but painted in transparent ink.

The audit passes when every font the page loads declares a strategy that keeps text visible: `swap`, `fallback`, or `optional`.

## What is font-display: swap?

`font-display: swap` tells the browser: render text immediately in a fallback system font, then swap in the web font whenever it arrives.

```css
@font-face {
  font-family: 'Inter';
  src: url('/fonts/inter-var.woff2') format('woff2');
  font-weight: 100 900;
  font-display: swap;
}
```

The text is readable from the first paint. The cost: when the web font arrives, the text re-renders, and if the two fonts have different metrics, the page can shift (see the CLS section below).

## How do I fix it?

Where the fix goes depends on where the font comes from:

**Self-hosted fonts:** add `font-display: swap` to each `@font-face` rule, as above. This is the case Lighthouse flags most often.

**Google Fonts:** add the `display` parameter to the stylesheet URL:

```html
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;700&display=swap" rel="stylesheet">
```

Without `&display=swap`, Google serves `@font-face` rules with no descriptor and the audit fails even though the CSS is not yours.

**Font services (Adobe Fonts, etc.):** most have a display setting in the project config. Adobe Fonts exposes it per-project; set it to swap.

**Third-party CSS you cannot edit:** override the face locally. Declare your own `@font-face` with the same `font-family` name, your own `src` pointing at the same file, and a `font-display` value. If you truly cannot, preloading the font file (below) shrinks the window where the strategy matters.

## Should I use swap, fallback, or optional?

| Value | Behavior | Use when |
|---|---|---|
| `swap` | Fallback text immediately, swap whenever the font arrives | Branding fonts that must eventually show. The default fix. |
| `fallback` | ~100 ms invisible, then fallback; swaps only if the font arrives within ~3 s | Middle ground: avoids a jarring swap 8 seconds in |
| `optional` | ~100 ms invisible, then fallback; the browser may skip the web font entirely on slow connections | Body text where the system font is acceptable. Best for performance and CLS. |
| `block` | Up to 3 s invisible, then fallback | Almost never. Icon fonts are the one honest case (see mistakes). |

The honest recommendation: `swap` for the one or two fonts that carry your brand, `optional` for everything else. If a font is so close to a system font that users would not notice it being skipped, `optional` means your slowest visitors simply never pay for it.

## Does font-display: swap cause layout shift?

It can. When the fallback font and the web font have different character widths or line heights, the swap moves content, and that movement counts toward [Cumulative Layout Shift](/audits/cumulative-layout-shift). Two mitigations:

1. **Tune the fallback with font metric overrides** so it occupies the same space as the web font:

```css
@font-face {
  font-family: 'Inter-fallback';
  src: local('Arial');
  size-adjust: 107%;
  ascent-override: 90%;
}
```

Tools like Capsize or the Fontaine build plugin compute these numbers for you.

2. **Preload the critical font** so it usually arrives before first paint, making the swap invisible:

```html
<link rel="preload" as="font" type="font/woff2" href="/fonts/inter-var.woff2" crossorigin>
```

Preload only the one or two files used above the fold. Preloading every weight defeats the purpose.

## What are common font-display mistakes?

- **`swap` on icon fonts.** The fallback render of an icon font is meaningless glyphs (tofu boxes or random letters). Icon fonts are the one case where `block` is defensible, and the better fix is replacing the icon font with inline SVG.
- **Adding the descriptor but not to every face.** Each weight and style is its own `@font-face` rule; Lighthouse flags each one individually.
- **Fixing your own CSS but not the Google Fonts URL.** The `&display=swap` parameter is the fix people miss most.
- **Preloading fonts that are not critical.** Each preloaded font competes with the LCP image for bandwidth.

## How do I verify the fix?

1. Re-run Lighthouse: "Ensure text remains visible during webfont load" should move to the passed list.
2. In DevTools, throttle the network to Slow 3G, hard-reload, and watch the first paint: text should be readable immediately in a fallback font.
3. If you chose `swap`, check the [CLS](/audits/cumulative-layout-shift) audit did not regress; if it did, add metric overrides to the fallback.

## Related audits

- [Cumulative Layout Shift (CLS)](/audits/cumulative-layout-shift), the metric a careless swap can hurt
- [First Contentful Paint (FCP)](/audits/first-contentful-paint), what invisible text delays
- [Eliminate render-blocking resources](/audits/render-blocking-resources), the other half of slow font delivery

---

Audit your URL at https://lighthouse-md.com.
