Het gebeurt op zowat elke website die ik bouw. Het heet layout shift, en het is op te lossen.
Wat er gebeurt
Een lettertype laden kost tijd. Tot het binnen is, moet de browser iets tonen, dus neemt hij een vervanger die al op het toestel staat: Arial, Times New Roman, iets dergelijks. Zodra het echte lettertype er is, wisselt hij om.
Geen twee lettertypes zijn even breed. Dezelfde zin past in Arial op drie regels, in jouw huisfont op vier, en alles daaronder moet dan een stuk opschuiven. Die verschuiving is niet subtiel: knoppen springen weg onder de cursor, beelden zakken omlaag net wanneer iemand wil klikken.
Voor het laden
Na het laden (zonder afstemming)
+34px sprong
Waarom het blijft gebeuren
De oplossing is bekend: geef de vervanger dezelfde maten als het echte lettertype. CSS heeft daar eigenschappen voor: size-adjust, ascent-override, descent-override. Het kan dus. Alleen rekent niemand die getallen met de hand uit.
@font-face {
font-family: "Inter";
src: url("/fonts/inter-variable.woff2") format("woff2");
font-weight: 100 900;
}
body {
font-family: "Inter", Arial, sans-serif;
}
Dit staat op bijna elke site. Het werkt, in de zin dat er tekst verschijnt, maar Arial is nooit gemeten tegen Inter. Niemand heeft de browser verteld hoe ver die twee uit elkaar liggen, dus rekent hij met de verkeerde maten tot het echte lettertype binnen is.
Het rekenwerk bestaat al
Capsize (opent in een nieuw venster) doet dat rekenwerk correct. Geef het de metingen van twee lettertypes en het levert de exacte CSS-waarden om de vervanger op maat te knippen. FontShim doet die som niet over. Een berekening die klopt, hoef je niet opnieuw te schrijven.
Maar Capsize is een rekenmachine, geen speurder. Je moet zelf aanleveren welke bestanden het moet meten. Op een site met een paar stylesheets, wat geïmporteerde fonts en een variabel lettertype erbij is dat al snel meer uitzoekwerk dan iemand ervoor over heeft.
Wat FontShim erbij doet
FontShim doet het speurwerk. Het leest je CSS, vindt elke @font-face-regel, haalt het bijbehorende bestand op en stuurt de echte metingen door naar Capsize. Wat eruit komt is een los CSS-bestand, en je bestaande stijlen blijven staan.
npx fontshim --css-dir src/styles --output-file fontshim.css
Dat is het hele commando. Geen account, geen dashboard, geen configuratiebestand dat je eerst moet leren kennen. Het antwoord is een bestand:
/* fontshim.css, gegenereerd, niet handmatig aanpassen */
:root {
--font-inter: "Inter", "Inter Fallback: Arial", Arial, sans-serif;
}
@font-face {
font-family: "Inter Fallback: Arial";
src: local("Arial");
size-adjust: 107.12%;
ascent-override: 90.20%;
descent-override: 22.30%;
line-gap-override: 0.00%;
}
Aan jouw kant verandert één regel:
body {
font-family: var(--font-inter);
}
Voor het laden
Na het laden (met FontShim)
0px sprong
Variabele lettertypes
Er is één plek waar ook Capsize het laat liggen. Een modern lettertype als Inter is vaak geen apart bestand per gewicht, maar één bestand dat elk gewicht tussen dun en vet kan tonen. Capsize meet daar één punt van, meestal het standaardgewicht, en past dat toe op alles.
Dat klopt niet, want vet is breder dan dun. Een vervanger die daar geen rekening mee houdt, is voor het ene gewicht te krap en voor het andere te ruim.
/* Gewicht 300, gemeten op de eigen instantie */
@font-face {
font-family: "Inter Fallback: Arial";
font-weight: 300;
size-adjust: 105.26%;
}
/* Gewicht 700, gemeten op de eigen instantie */
@font-face {
font-family: "Inter Fallback: Arial";
font-weight: 700;
size-adjust: 110.19%;
}
Waar de meeste tools beide gewichten dezelfde 107,12% geven, meet FontShim elk gewicht apart. Op papier een klein verschil. Op een pagina met vette koppen en dunne bodytekst zit daar het verschil tussen kloppen en ongeveer kloppen.
De gedachte erachter
FontShim probeert nergens slimmer te zijn dan nodig. Het herschrijft geen rekenwerk dat al bestaat, en het vraagt niets wat je toch niet kan weten. Welk lettertype er op het toestel van je bezoeker staat, weet jij niet. Dus genereert FontShim er meteen een per platformfont, in plaats van je te laten kiezen.
Het onthoudt niets, slaat geen lettertypes op en praat met geen enkele server nadat het klaar is. Eén commando erin, één bestand eruit. Een tool die precies één ding doet, is makkelijker te vertrouwen dan een tool die alles belooft.
Dat is ook hoe ik zelf aan sites werk. Liever een probleem goed oplossen dan het onzichtbaar houden achter een workaround. Je merkt dat soort keuzes niet wanneer ze kloppen. Je merkt ze wel op het moment dat ze niet kloppen, en dan is het te laat om nog uit te leggen dat het anders had gekund.
Tot slot
Sites verspringen niet omdat ontwikkelaars slordig zijn. Ze verspringen omdat niemand ooit heeft uitgerekend hoeveel ruimte een vervangend lettertype nodig heeft, en omdat dat met de hand doen meer tijd kost dan een project ervoor over heeft.
Voor je bezoeker is dat het soort werk dat nooit opvalt als het klopt, en meteen stoort als het niet klopt.
npx fontshim --css-dir src/styles --output-file fontshim.css
Eén regel. Geen sprong meer.
Werk je zelf aan sites of tools zoals deze? De code en de volledige technische uitleg staan op GitHub. Wil je erover praten, dat kan altijd.