Writing

The fallback font is matched on width, not x-height

2 min

What the recipe is for

This site loads one subset webfont, and until it arrives the text sets in a fallback. If the fallback occupies different boxes than the real face, the swap reflows the page — and the layout-shift budget here is zero. The fix is a documented technique: re-declare a local font inside @font-face with size-adjust and friends, so the fallback already occupies the box the real face will occupy.

The published recipe computes size-adjust from x-height, because x-height governs perceived size. That reasoning sounded slightly off for a layout problem, but it was the documented technique and I nearly shipped it on faith. I measured it first.

So I measured it

The x-height recipe made the fallback 55px narrower than the real face on the headline. Plain, unadjusted Arial — doing nothing at all — was only 16px off. Following the recipe would have been worse than ignoring it, which is the kind of result you only get because someone checked.

What reflows a page is not how tall letters sit; it is how wide a run of text sets. So size-adjust here is computed from advance width, and Arial’s metrics are read off this machine rather than taken from published tables.

Measured after

Vertical boxes identical between the two faces. A wrapped paragraph breaks to the same line count in the real face, the matched fallback, and raw Arial. Body width lands 0.9% off with the matching, against 2.1% without.

The second-order benefit only shows up later: because the wrap is identical, a ch-based measure is almost safe. Almost — one hero lede still tipped a word at exactly one viewport and cost 0.012 of CLS, which is why that lede’s measure is now pinned in rem. The fallback gets you to 0.9%. The remaining 0.9% is yours to engineer away.

All writing