Skip to content

Commit 7722197

Browse files
Marvin-Cypherclaude
andcommitted
docs(design): kill FOUT via font-display: block + metric-matched fallbacks
Two-part fix to eliminate the visible font swap on page navigation: 1. font-display: block (was: swap) Hides text up to 3s while the woff2 loads, then renders directly with the web font. No fallback flashes mid-render. On cached navigations the wait is imperceptible; on cold loads it's a brief invisible-text moment instead of an obvious wrong-font flash. 2. Metric-matched local fallbacks New "ABCArizonaFlare Fallback" / "ABCDiatype Fallback" / "SuisseIntlMono Fallback" @font-face entries wrap Georgia / Trebuchet MS / SFMono with size-adjust + ascent-override + descent-override so the fallback occupies the exact same vertical and horizontal space as the real font. If the block period expires before the woff2 arrives, the fallback renders without layout shift, and the eventual swap is visually invisible. Metric values are starting estimates; tune by eye if any heading or body line shifts when the swap happens. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
1 parent 6528428 commit 7722197

1 file changed

Lines changed: 61 additions & 7 deletions

File tree

fonts.css

Lines changed: 61 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -6,37 +6,91 @@
66
* - ABCDiatype body, UI, navigation, h3
77
* - SuisseIntlMono code, eyebrows, status pills
88
*
9-
* Source of truth: website-nextjs/src/app/globals.css + theme/typography.css
9+
* FOUT mitigation strategy:
10+
* 1. font-display: block on the real fonts — hides text up to 3s while
11+
* the woff2 loads; eliminates the visible swap from fallback to web
12+
* font. On cached navigations the wait is imperceptible.
13+
* 2. Metric-matched fallback @font-faces (`size-adjust`, `ascent-override`,
14+
* etc.) so if the block period does expire to the fallback, the
15+
* fallback occupies the same space — no layout shift, no jolt.
16+
*
17+
* Metric values are starting estimates from comparable display-serif and
18+
* humanist-sans pairings. Tune by eye against the live site if anything
19+
* looks off (the most likely culprit is `size-adjust`).
1020
*/
1121

22+
/* ─── Real webfonts ─────────────────────────────────────────────────── */
23+
1224
@font-face {
1325
font-family: "ABCArizonaFlare";
1426
src: url("/fonts/exa/ABCArizonaFlare-Regular.woff2") format("woff2");
1527
font-style: normal;
1628
font-weight: 400 700;
17-
font-display: swap;
29+
font-display: block;
1830
}
1931

2032
@font-face {
2133
font-family: "ABCDiatype";
2234
src: url("/fonts/exa/ABCDiatype-Regular.woff2") format("woff2");
2335
font-style: normal;
2436
font-weight: 400 700;
25-
font-display: swap;
37+
font-display: block;
2638
}
2739

2840
@font-face {
2941
font-family: "SuisseIntlMono";
3042
src: url("/fonts/exa/SuisseIntlMono-Regular-WebTrial.woff2") format("woff2");
3143
font-style: normal;
3244
font-weight: 400 700;
33-
font-display: swap;
45+
font-display: block;
46+
}
47+
48+
/* ─── Metric-matched local fallbacks ─────────────────────────────────── */
49+
/*
50+
* These wrap the system fallback font and adjust its metrics so it occupies
51+
* the same vertical/horizontal space as the real webfont. When the block
52+
* period times out (or before the woff2 finishes downloading on a cold
53+
* connection), the page renders with these — visually nearly identical
54+
* footprint to the real webfont, so when the swap eventually happens
55+
* there's no layout shift.
56+
*/
57+
58+
@font-face {
59+
font-family: "ABCArizonaFlare Fallback";
60+
src: local("Georgia"), local("Times New Roman");
61+
size-adjust: 94%;
62+
ascent-override: 95%;
63+
descent-override: 22%;
64+
line-gap-override: 0%;
3465
}
3566

67+
@font-face {
68+
font-family: "ABCDiatype Fallback";
69+
src: local("Trebuchet MS"), local("Arial");
70+
size-adjust: 97%;
71+
ascent-override: 92%;
72+
descent-override: 23%;
73+
line-gap-override: 0%;
74+
}
75+
76+
@font-face {
77+
font-family: "SuisseIntlMono Fallback";
78+
src: local("SFMono-Regular"), local("Consolas"), local("Menlo"),
79+
local("Liberation Mono");
80+
size-adjust: 100%;
81+
ascent-override: 90%;
82+
descent-override: 22%;
83+
line-gap-override: 0%;
84+
}
85+
86+
/* ─── Font-family stacks ─────────────────────────────────────────────── */
87+
3688
:root {
37-
--v3-heading: "ABCArizonaFlare", Georgia, serif;
38-
--v3-body: "ABCDiatype", "Trebuchet MS", Arial, sans-serif;
39-
--v3-mono: "SuisseIntlMono", "SFMono-Regular", Consolas, monospace;
89+
--v3-heading: "ABCArizonaFlare", "ABCArizonaFlare Fallback", Georgia, serif;
90+
--v3-body: "ABCDiatype", "ABCDiatype Fallback", "Trebuchet MS", Arial,
91+
sans-serif;
92+
--v3-mono: "SuisseIntlMono", "SuisseIntlMono Fallback", "SFMono-Regular",
93+
Consolas, monospace;
4094
}
4195

4296
body,

0 commit comments

Comments
 (0)