perf: font-display: optional eliminates CLS from font swap#6
Merged
Conversation
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>
There was a problem hiding this comment.
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-facerules (Source Serif 4 roman/italic and Inter roman/italic) to usefont-display: optionalinstead ofswap.
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Why
Lighthouse Insights reported two related issues on the deployed site:
<p class="lede">named as the layout-shift culprit, web font named as the trigger).Both are the same root cause:
font-display: swappaints a fallback first, then swaps in the real font, causing reflow.What
Switch all four self-hosted
@font-facedeclarations (Source Serif 4 roman + italic, Inter roman + italic) fromfont-display: swaptofont-display: optional. Withoptional: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)
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-overridefallback@font-facethat matches Source Serif 4's metrics so the swap is invisible — more code, more polish, same Lighthouse outcome.