The Font That Waits Its Turn: Public-Sector Type Without the Jolt
Photo: webfont loading browser performance public sector website UK, via i.pinimg.com
Public-sector websites in the UK have a type problem that is only partly about looks. A council page, an NHS leaflet hub or a GOV.UK-style service can look calm on a fast office connection and then jolt on a phone in a queue at a leisure centre. The usual suspect is not the font file itself but the order in which it arrives. GDS Transport, or whatever approved typeface your organisation uses, should not be allowed to shove the layout around while it turns up.
This is not an argument for bland typography. It is an argument for sequencing. Load the one weight that carries the first screen, let it swap predictably, and hold the rest until the page is usable. That is the whole trick.
Check the licence before the loader
Before you self-host anything, check what you are allowed to redistribute. GDS Transport is not a generic open font you can hand to every supplier; it is tied to GOV.UK usage and licensing. If your organisation has permission, fine. If not, pick an open alternative and test it against your brand guidance. The loader does not care which file it preloads, but your legal team will care which file you copied.
Once you have a file you can serve, subset it. A full Latin font with every accent and symbol is usually unnecessary for a service that needs English and Welsh. Tools such as pyftsubset can strip unused glyphs and keep the ranges you actually need. Keep the Welsh characters. Keep the punctuation. Drop the rest.
One critical weight, not the whole family
The fastest font request is the one you do not make on the critical path. Preload exactly one file: the regular weight used above the fold. Do not preload bold, italic, or the display face unless you have measured that they are needed for first paint. Preloading everything is just a queue with better branding.
A simple head tag looks like this:
<link rel='preload' href='/fonts/public-sans-regular.woff2' as='font' type='font/woff2' crossorigin>
The CSS should set a predictable display policy. For body text, font-display: swap is usually the sane compromise: text is readable immediately in the fallback, then swaps when the real font arrives. For a large display headline that would look awful in fallback, optional can be better because it lets the browser skip the swap on a slow connection. Do not use block for a public service unless you enjoy watching people stare at invisible text.
Give the fallback a fighting chance
A swap is only calm if the fallback and the real font occupy similar space. Use a fallback @font-face with metric overrides so the browser reserves roughly the right line height and width. You do not need pixel-perfect matching. You need to stop the paragraph from reflowing by three lines when the font lands.
@font-face {
font-family: 'Public Sans Fallback';
src: local('Arial');
size-adjust: 102%;
ascent-override: 92%;
descent-override: 22%;
line-gap-override: 0%;
}
Tune those numbers against your actual font with a test page and a slow connection. Then load the remaining weights after the page is interactive. The FontFace API is good for this because you can ask for a font only when a component needs it:
if ('fonts' in document) {
const bold = new FontFace('Public Sans', 'url(/fonts/public-sans-bold.woff2)', { weight: '700' });
bold.load().then(font => document.fonts.add(font));
}
Wrap that in requestIdleCallback or a user interaction if the bold weight is only used below the fold. Do not make every visitor pay for a font that only appears in a cookie banner.
Welsh diacritics are not an edge case
A subset that drops ŵ, ŷ, â, ê, î, ô and û is not a clever optimisation. It is a bug that shows up in place names, personal names and bilingual copy. If your service covers Wales, test with real strings: Môn, ŵyr, Caerdydd, Llanfairpwllgwyngyll. Check the rendered glyphs, not just the HTML source.
The practical approach is to keep the Latin Extended-A range for the Welsh characters and the Latin-1 Supplement for the common accents. If you are splitting files by unicode-range, make sure the Welsh range is in the critical file or in a file that loads before the user reaches the content. A missing diacritic is more embarrassing than a slightly larger font file.
Measure the jolt, do not imagine it
Open the page on a throttled connection and watch the layout. Use your browser performance panel, Lighthouse, or WebPageTest with a UK location. Look at cumulative layout shift, first contentful paint and the moment the font swaps. If the page jumps, adjust the fallback metrics or change the display policy. If the font arrives late and the page is already readable, that is usually fine.
Also check the network waterfall. A preload that is ignored, a font served with the wrong MIME type, or a missing crossorigin attribute will waste the hint. Fonts are subject to CORS, so the preload and the actual request need to match.
What to ship on Monday
Start with one regular weight, subsetted, self-hosted and preloaded. Add metric-adjusted fallbacks. Load bold and italic only when needed. Test Welsh diacritics on a real page. Then measure. The goal is not a font-loading masterpiece. The goal is a public-sector page that feels steady on a train, in a council office and on a phone in the rain.