Why custom type flashes, jumps and shifts
When a browser reaches a @font-face rule, it has to discover the font file, download it, then repaint the text. What happens during that wait is where the trouble starts. Some browsers historically hid text until the font arrived (a flash of invisible text, known as FOIT); others showed a system fallback and then swapped it (a flash of unstyled text, FOUT). Either behaviour is jarring, and if your custom font takes up more or less space than the fallback, the whole page reflows the moment it lands.
That reflow is measured. Google's Core Web Vitals include Cumulative Layout Shift (CLS), and font swapping is one of the most common causes of a poor score. Fonts also delay Largest Contentful Paint when a headline is the biggest element on screen. For a UK small business competing on local search, this is not cosmetic: page experience feeds into rankings, and slow text feeds into bounce rates.
The weight adds up fast, too. A typical brand loads a headline face and a body face in four or five weights, which can easily mean 300 KB or more of font data before a single image arrives.
font-display: the one-line improvement
The font-display descriptor tells the browser what to do while a font downloads. If you take one thing from this article, add it to every @font-face rule on your site.
- swap: show fallback text immediately, then swap in the custom font when it arrives. The best default for body copy, because text is always readable.
- optional: use the custom font only if it is already cached or arrives almost instantly; otherwise keep the fallback for this visit. The zero-shift option, ideal for visitors on slow connections.
- fallback: a compromise. A very short invisible period, then the fallback, with only a brief window in which a swap is allowed.
- block: hide the text while waiting. Almost never what you want, and only defensible for icon fonts, which are better replaced with SVGs anyway.
If you load fonts from Google Fonts, the embed code has included display=swap in the URL for years, but older copy-pasted snippets may predate it. It takes thirty seconds to check yours.
Need a hand with this?
Our team delivers Web Design for UK businesses — with a free initial consultation, transparent fixed quotes and no lock-in contracts. Tell us what you're working on →
Self-host your fonts, even the free ones
There used to be an argument that loading from Google's CDN was faster because visitors already had popular fonts cached from other websites. That stopped being true when browsers introduced cache partitioning: caches are now split per site, so every visitor downloads your fonts fresh regardless. What remains is pure cost: a DNS lookup and TLS connection to two extra domains before a single glyph can render.
There is a compliance angle as well. A German court ruled in 2022 that embedding Google Fonts remotely passed visitor IP addresses to Google without consent. UK GDPR is not identical to the German position, but self-hosting removes the question entirely.
Doing it is straightforward: download the WOFF2 files (google-webfonts-helper is a long-standing free tool for this), upload them to your own domain, write @font-face rules with font-display set, and give the files a long cache lifetime such as one year, since fonts rarely change.
Subsetting: stop shipping alphabets nobody reads
A full font file often includes Cyrillic, Greek, Vietnamese and hundreds of ligatures and symbols your site will never render. Subsetting strips the file down to the characters you actually need: for most UK sites, that is basic Latin plus punctuation, proper quotation marks and the £ sign. A subset file is commonly a fraction of the original's size.
Free tools do the work: pyftsubset (part of the fonttools package) or glyphhanger, which crawls your pages and builds the subset for you. If you keep multiple subsets, the unicode-range descriptor lets the browser download only the ranges a page actually uses, which is exactly how Google Fonts serves its own files.
One caution: if your site shows user-generated content or customer names, keep the Latin Extended range so accented characters do not fall back to a different typeface mid-word.
Variable fonts: five files become one
Traditional families ship every weight as a separate file: regular, medium, semibold, bold, and so on. A variable font packs the whole weight axis into one file, so a single download covers everything from 100 to 900, including in-between values static files cannot offer. The variable file is larger than any single static weight but nearly always smaller than three or four of them combined.
Many popular open-source faces, including Inter, Source Sans and Fraunces, ship variable versions. In CSS you declare the supported range once inside the @font-face rule (font-weight: 100 900), then use any weight in your stylesheets as normal.
Kill the jump: fallback tuning and preload
font-display: swap keeps text visible, but the swap itself still moves the layout if the two fonts differ in width. Modern CSS fixes this with the size-adjust, ascent-override and descent-override descriptors: you define a local fallback such as Arial, scaled to occupy the same space as your web font, so the swap becomes visually silent. Free generators such as the Fallback Font Generator, or the Capsize library for developers, calculate the values for you.
Finally, preload the files that matter. A preload link tag in the head (rel="preload", as="font", type="font/woff2", plus the crossorigin attribute, which is required even for same-origin fonts) tells the browser to fetch the font before it has finished parsing your CSS. Preload only the one or two files used above the fold; preloading everything defeats the purpose and delays other resources.
Key Takeaway
Self-host WOFF2 files, add font-display: swap, and preload only the one or two files used above the fold. Subset each font to the Latin characters you actually use, replace multiple weights with a single variable font, and tune your fallback with size-adjust so the swap does not move the layout. Done together, these steps routinely cut font weight to a fraction of its former size and remove font-driven layout shift entirely.
A worked before-and-after
Here is a representative example of the pattern, with illustrative numbers from a typical brochure-site rebuild.
Before
- Two Google-hosted families in five weights: roughly 350 KB across seven files.
- Two extra origins (fonts.googleapis.com and fonts.gstatic.com) connected before any text rendered.
- The headline repainted visibly and pushed the hero content down, putting CLS into the 'needs improvement' band.
- Largest Contentful Paint waited on the headline font over 4G.
After
- One self-hosted variable font, subset to Latin: a single file of around 40 to 50 KB.
- Preloaded from the same domain, with no extra connections.
- font-display: swap paired with a size-adjusted Arial fallback: no measurable font-driven layout shift.
- Text painted on first render, and the swap became imperceptible.
The whole exercise takes a competent developer an afternoon, and the gains survive any redesign that keeps the same typefaces. If you would rather not spend your afternoon inside @font-face rules, our web team handles this as part of every performance review.
