Skip to content

perf: font-display: optional eliminates CLS from font swap#6

Merged
CybotTM merged 1 commit intomainfrom
perf/font-display-optional
May 10, 2026
Merged

perf: font-display: optional eliminates CLS from font swap#6
CybotTM merged 1 commit intomainfrom
perf/font-display-optional

Conversation

@CybotTM
Copy link
Copy Markdown
Owner

@CybotTM CybotTM commented May 10, 2026

Why

Lighthouse Insights reported two related issues on the deployed site:

  • CLS 0.021 attributed to the index lede paragraph shifting when Source Serif 4 swapped in over the system fallback (<p class="lede"> named as the layout-shift culprit, web font named as the trigger).
  • LCP element render delay 200 ms on the same paragraph — TTFB is 0 ms, so the delay is the browser holding layout while it waits to commit on a font.

Both are the same root cause: font-display: swap paints a fallback first, then swaps in the real font, causing reflow.

What

Switch all four self-hosted @font-face declarations (Source Serif 4 roman + italic, Inter roman + italic) from font-display: swap to font-display: optional. With optional:

  • Browser blocks ~100 ms waiting for the font.
  • If it arrives in time → use it from first paint, no fallback ever rendered → no swap → no shift.
  • If not → fallback for whole session, web font cached for next visit. Still no swap.

Both fonts are already preloaded with <link rel=preload as=font href="…" crossorigin> in <head> and weigh ~50 KB woff2 each, so on any normal connection they arrive comfortably inside the 100 ms window and the web font is what users see on first visit.

Lighthouse before vs after (local desktop preset, all 3 pages)

Metric Before After
CLS 0.021 0
LCP ~0.5 s with swap delay 0.4 s
layout-shifts items 1 (the lede) 0
Performance / A11y / Best practices / SEO 100 / 100 / 100 / 100 100 / 100 / 100 / 100

Trade-off

A first-time visitor on a connection so slow the woff2 doesn't arrive in 100 ms will see the system serif/sans fallback for that whole session, with the web font kicking in on their next page navigation (the font is by then cached). On every subsequent visit, the browser uses the cached font immediately. For a CV that's typically loaded once per visitor and where the editorial typography is desirable but not load-bearing, this is the right trade.

If brand-locked rendering ever matters more than zero-CLS, the alternative is a size-adjust / ascent-override fallback @font-face that matches Source Serif 4's metrics so the swap is invisible — more code, more polish, same Lighthouse outcome.

The Lighthouse CLS audit reported a 0.021 layout shift on the index
lede, and the LCP breakdown attributed a 200 ms element render delay
to the same paragraph — both from Source Serif 4 swapping in over the
system serif fallback after first paint.

Switching all four self-hosted @font-face declarations from
font-display: swap to font-display: optional eliminates the swap step
entirely:

- Browser blocks ~100 ms waiting for the (preloaded) font.
- If the font arrives in time, it is used from first paint — no
  fallback ever rendered, no swap, no shift.
- If the font does not arrive in 100 ms, the fallback is used for the
  rest of the session and the web font is cached for the next visit.

Our 50 KB woff2 files are <link rel=preload as=font crossorigin>'d in
<head>, so on any normal connection they arrive well within 100 ms and
the web font is the one users see on first visit.

Local Lighthouse desktop preset, post-change, all three pages:
- categories: 100 / 100 / 100 / 100
- cumulative-layout-shift: 0   (was 0.021)
- largest-contentful-paint: 0.4 s
- layout-shifts items: 0       (was 1: the lede)

Visual identity preserved: Source Serif 4 still renders on every test
page; the variable-weight italic and Inter UI fonts are unchanged
beyond the same display swap → optional substitution.

Signed-off-by: Sebastian Mendel <info@sebastianmendel.de>
Copilot AI review requested due to automatic review settings May 10, 2026 15:52
Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR addresses Lighthouse-reported CLS and LCP render delay caused by font-display: swap triggering a late font swap/reflow, by changing the font loading strategy to avoid swapping after first paint.

Changes:

  • Updated all four self-hosted @font-face rules (Source Serif 4 roman/italic and Inter roman/italic) to use font-display: optional instead of swap.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

@CybotTM CybotTM merged commit eff8a5e into main May 10, 2026
7 checks passed
@CybotTM CybotTM deleted the perf/font-display-optional branch May 10, 2026 15:54
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants