WebFonts slim laden voor een betere performance

Praktisch iedere moderne nieuwe website maakt dankbaar gebruik van Custom Webfonts. Dat zijn fonts die niet standaard in de browser aanwezig zijn maar eigen custom fonts die ingeladen worden door de browser tijdens het laden.

Met Web Fonts fonts bouw je een veel mooiere webervaring maar deze fonts veroorzaken ook veel problemen bij het laden van pagina's, zoals onzichtbare tekst, langzame laadtijd, geblokkeerde weergave en verspringende lay-out. 

Ik zie dat veel ontwikkelaars deze problemen negeren of misschien steeds maar dezelfde fouten maken alleen maar omdat 'ze dat altijd hebben gedaan. Dat is zonde, want met een goede webfont strategie voorkom je veel problemen. Bovendien is het minder werk dan je zou denken.

Fonts geladen met Google fonts en display:swap font synthesis voorbeeld Geavanceerd font-laad techniek font synthesis voorbeeld

In dit artikel leer je hoe je webfonts sneller en slimmer kunt laden. We gaan het hebben over

  • Gebruik het juiste font-formaat
  • Gebruik de juiste 'font-face declaration'
  • Lettertypen vooraf laden
  • Vermijd onzichtbare tekst tijdens het laden van lettertypen.
  • Voorkom reflow / lay-out shift
  • Geavanceerde font-laad technieken

Laten we deze punten een voor een uitsplitsen.

1. Gebruik het juiste font-formaat

Er zijn een aantal font-formaten die je kunt gebruiken (eot, ttf, woff, svg, woff2). Hoe weet je welke je moet gebruiken? Dat is eenvoudig, op dit moment hoef je eigenlijk alleen woff en woff2 te ondersteunen.
Woff is een font formaat dat is ontwikkelt door Google. Woff is standaard al gezipped en mede daardoor sneller en beter dan alle eerdere formaten. Woff wordt ondersteund door alle moderne browsers. Woff2 is een snellere, kleinere en nog verder verbeterde versie van het woff formaat maar wordt niet ondersteund door internet explorer.

2. Het lettertype declareren

Om een eigen lettertype te gebruiken moet je het een naam geven samen met enkele eigenschappen (oa het gewicht en de stijl). Het is vrij eenvoudig om een lettertype te declareren en dat doe je als volgt:

@font-face {  
   font-family: 'myFont';  
   font-weight: 400;  
   font-style: normal;  
   font-display: swap;
   unicode-range: U+000-5FF 
   src: local('myFont'),
url('/fonts/myFont.woff2') format('woff2'), url('/fonts/myFont.woff') format('woff');
}

In het bovenstaande voorbeeld zie je dat we een aantal style attributen gebruiken waarmee invloed hebben op hoe het font geladen en gebruikt wordt.

  • font-family is de naam die we het font geven wanneer we daar later in de CSS of via JavaScript naartoe verwijzen.
  • font-weight is de dikte die het font heeft. 400 is normaal, alles daarboven is dikgedrukt en alles daaronder dunne tekst
  • font style geeft de stijl aan, dit kan normal,italic of oblique (schuin) zijn.
  • font-display is een belangrijke eigenschap voor hoe het font zich gedraagt tijdens het laden van het font. Hiermee bepaal je hoe lang een lettertype onzichtbaar blijft (block) en wanneer het tijdelijk vervangen mag worden door een alternatief systeem-lettertype wanneer het laden van het font te lang duurt
  • src geeft aan waar de browser mag zoeken naar het lettertype en in welke volgorde.

Nu we dit weten kunnen we onze eerste slimmigheden inbouwen. Er zijn hier namelijk op 5 punten flinke snelheidswinst te behalen.

Vermijd onzichtbare tekst tijdens het laden

De tekst van een pagina (om specifiek te zijn: text DOM-nodes) worden standaard niet op het scherm gezet totdat het font is geladen. De tekst op het scherm is, totodat het webfont is geladen, onzichtbaar.

Wanneer het font is geladen zie je de tekst op het scherm knipperen. Dat is de Flash Of Invisible Text of de FOIT. Vaak wil je deze Fash Of Invisible Text voorkomen. Waarom? Omdat een lege pagina niet iets is waar een bezoeker op zit te wachten. De FOIT hangt ook vaak samen met belangrijke snelheids metrics. Vaak hangt de first meaningful paint samen met de FOIT.

Via font-display:swap kun je aangeven dat een browser tijdens het laden van een font het font mag vervangen door een systeem font. Zodra het font is geladen verspringt het font naar het uiteindelijke lettertype. Dat heeft de Flash Of Unstyled Text of de FOUT. We komen hier later nog op terug bij de geavanceerde font laad strategieën.

Geef de Unicode range op

Via de unicode range geef je aan welke tekens (glyphs) het nog-te-dowloaden webfont bevat. Er zijn duizenden unicode tekens zoals 👽 (👽), die door de browser gebruikt kunnen worden. 

Door de unicode range mee te geven weet een browser of het font ge-download moet worden voor ieder teken. Geef je de unicode range niet mee dan zal een browser van boven naar onder alle kandidaatfonts uitproberen om te kijken of dit teken voorkomt in het font. 
Glyphs range generator van Eli Fitch ?

Maak gebruik van  font synthesis

Stel dat je van een lettertype font-weight:400 gebruikt voor standaard tekst en font-weight:700 voor headings? Dat zijn 2 fonts die je laadt. Maar wat nu als er een italic en dik gedrukt woord in de tekst voorkomt? Dan zou je eigenlijk, om het compleet te maken, 2 extra fonts moeten toevoegen. Font-weight:600 voor dik gedrukte tekst en font-style:italic voor italc tekst. Jouw fontface set wordt dan zomaar 2x zo groot.

font synthesis voorbeeld Font synthesis voorbeeld

Wil je dat liever niet dan kan de browser het font zelf namaken door font-synthesis. Bij font synthesis wordt italic en dikgedrukte tekst afgeleid van normale (400) tekst. Niet ideaal natuurlijk voor een designer maar een technisch slimme oplossing en in sommige gevallen een goede trade-off.

Verklein fonts met font subsetting

Wil je nog een stapje sneller? Denk dan eens of je echt alle tekens in een fontset wel echt nodig hebt. Bijvoorbeeld de 'Ç'. Heb je die echt nodig in jouw headings? Is het antwoord nee? Dan kun je jouw fonts inkorten (een subset maken) via deze site lettertype-generator

font glyphs Deze glyphs zijn standaard aanwezig in een latin font

Kies de juiste en snelste font volgorde

De volgorde van de fonts in het src attribuut kan een wereld van verschil maken. Een browser werkt van boven naar beneden. Kan dat font niet gevonden worden of wordt dat font niet ondersteund? Dan probeert een browser het volgende font. 

De snelste volgorde is als volgt: Eerst lokaal zoek je lokaal via local('myfont'). Daarna de woff2 (weet je nog, deze is kleiner dan woff) en dan pas de woff. Dit zorgt er voor dat we altijd de snelste variant van het lettertype gebruiken. Verwar local() trouwens niet met de lokale browser cache, local kijkt naar lokaal geïnstalleerde lettertypen. Vaak hebben telefoons al een flink aantal lettertypen voor geïnstalleerd.

Wil je echt andere formaten gebruiken zoals eot of ttf zet deze dan altijd achteraan

3. Lettertypen 'preloaden'

Vaak wil je dat lettertypen zo snel mogelijk beschikbaar zijn voor de browser. Helaas doen browsers dat niet uit zichzelf. Standaard zal een browser een lettertype pas downloaden wanneer dat lettertype op het scherm (of specifieker in een zichtbare DomNode) gebruik gaat worden. Daarvoor moet een browser eerst de CSS bekijken en besluiten dat het element met het font ook werkelijk op de pagina te vinden is.

Met resource hints kun je dat gedrag van de browsers aanpassen.;Resource hints in de vorm van rel="preload" zorgen er voor dat dat de fonts met hoge prioriteit worden gedownload. Een browser begint direct met het downloaden van een bron als het zo'n hint tegenkomt. Je hoeft dus niet te wachten op het analyseren van de CSS en HTML.

Een font 'preloaden' met resource hints doe je als volgt: Plaats deze code zo hoog mogelijk in de head van de pagina, nog voor de CSS en JavaScript verwijzingen.

<link 
    rel="preload" 
    as="font" 
    href="/my-font.woff2" 
    type="font/woff2" 
    crossorigin="anonymous"
>

In het bovenstaande voorbeeld zie je dat het link element verschillende attributes kent.

  • rel="preload"geeft aan dat de bron alvast compleet geladen mag worden. Een alternatief zou zijn rel="preconnect". Dat betekent dat de verbinding alvast gestart kan worden naar de server maar dat er nog niets gedownload hoeft te worden.
  • as="font" vertelt de browser dat het type een font is. Dat lijkt misschien overbodig maar wanneer je dit niet meegeeft zal de browser het font waarschijnlijk 2 maal ophalen.
  • href="/my-font.woff2" verwijst naar de locatie van het te donwloaden bestand
  • type="font/woff2" geet het mimetype van het font aan. Dit mimetype moet, net zoals het as="font" attribuut altijd opgegeven worden. Doe dat niet, dan weet de browser niet 100% zeker of de bron ingezet kan worden als font en wordt het nogmaals gedownload.
  • crossorigin="anonymous" geeft aan dat het font via CORS systeem moet worden opgehaald omdat de specificaties voor het ophalen van fonts nu eenmaal zo zijn opgesteld. Ik kan er ook niets aan doen :-)

Pas op met het gebruik van een CDN:
Nu je dit weet kunnen we je vertellen waarom we geen fan zijn van extern gehostse fonts op CDN's zoals fonts.google.com. Toegegeven, het toevoegen van een webfont is door tools als dit een peulenschil geworden maar het gaat flink ten koste van jouw snelheid.

Lettertypes op Google Fonts worden regelmatig bijgewerkt. Je kunt er daarom niet op vertrouwen dat de 'preloaded link' op Google de laatste versie van het font is. Misschien staat er in de CSS een ander bestand en worden er uiteindelijk 2 versies van het font gedownload.
Wil je echt, ondanks dat we het afraden, Google font gebruiken dan kun je via rel="preconnect" toch iets van de verloren snelheid terughalen.

Zelf een font hosten is dus bijna altijd sneller. Let wel op dat je gebruik maakt van http/2 wanneer je zelf jouw fonts wilt hosten. Dat zorgt voor een flinke snelheidsboost omdat fonts eerder en in parallel gedownload kunnen worden.

Wil je nog wat extra snelheidswinst behalen door een subset te gebruiken of een slimme laadmethode? Dan heb je via een CDN ook niet genoeg controle. Maar daar komen we nog op terug.

4. Geavanceerde font laad technieken

Nu je alles weet wat je moet weten over het laden van fonts kunnen we verder naar de meer geavanceerde technieken.

Fonts geladen met Google fonts en display:swap font synthesis voorbeeld WebFonts slimmer ladenfont synthesis voorbeeld

Bij deze technieken vind je naast uitleg ook code die je kunt gebruiken. Je kunt deze code in de <head> van de pagina plaatsen

De 'bare minimum'

Bij de brae minimum techniek maak je slim gebruik van de aanwezige browser functies zoals preloading, font-display:swap. Deze techniek gaat uit van de browser en is vooral effectief bij het laden van een klein aantal fonts (1 tot 2) die op jouw eigen server zijn gehost. 

Via preloading laad je de fonts in zodat ze al vroeg tijdens het laden aanwezig zijn en via de font-display: swap; zorg je dat bezoekers geen lege pagina te zien krijgen. Voor terugkerende bezoekers (die de fonts al in de browser cache) hebben staan is dit de snelste techniek terwijl het voor nieuwe bezoekers een acceptabel korte FOUT oplevert

Voordelen: Deze techniek leunt 100% op de browser en laat de browser zelf het zware werk doen. Er zijn geen extra scripts of classes nodig om dit te laten werken.
Nadelen: Flash Of Unstyled Text en voor ieder font een Content Reflow

<link rel="preload" href="/myFont400.woff2" 
      as="font" type="font/woff2" crossorigin>
<link rel="preload" href="/myFont700.woff2" 
      as="font" type="font/woff2" crossorigin>

<style>
@font-face {
    font-family: 'myFont';
    font-weight:400;
    display:swap; /*enabled fallback font and FOUT*/
    src: local('myfont'),url('myFont400.woff2') format('woff2');
}
@font-face {
    font-family: 'myFont';
    font-weight:700;
    display:swap; /*enabled fallback font and FOUT*/
    src: local('myfont'),url('/myFont700.woff2') format('woff2');
}
body {
    // myFont + fallback
    font-family: 'myFont',sans-serif;
    font-weight:400;
}
h1,h2,h3,h4,h5,h6{
    font-weight:700;
}
</style>


De 'Font with a class'

Bij deze techniek laad je de fonts met JavaScript en zodra alle fonts zijn geladen voeg je een class toe aan de pagina. Via die class wordt het custom font geactiveerd. Voor terugkerende bezoekers sla je in de sessionstorage (of localstorage als je wilt) dat de fonts al geladen zijn 
Voordelen: Relatief eenvoudig toe te passen, geen aanpassingen aan de fonts zelf nodig, javascript zorgt voor parallelle netwerkverzoeken, elimineert de FOIT zonder font-display en er is slecht 1 Flash Of Unstyled Text (FOUT)
Nadelen: Ten opzichte van de 'bare minimum' is dit lastiger te onderhouden en de fonts moeten ergens hard-coded in staan, bijvoorbeeld in JavaScript

<link rel="preload" href="/myFont400.woff2" 
      as="font" type="font/woff2" crossorigin>
<link rel="preload" href="/myFont700.woff2" 
      as="font" type="font/woff2" crossorigin>
<style>
@font-face {
    font-family: 'myFont';
    font-weight:400;
    src: local('myfont'),url('/myFont400.woff2') format('woff2');
}
@font-face {
    font-family: 'myFont';
    font-weight:700;
    src: local('myfont'),url('/myFont700.woff2') format('woff2');
}
body {
    // fallback
    font-family: sans-serif;
}
html.fl body{
    // web font
    font-family: 'myFont';
}
</style>
<script>
(()=>{
    if( "fonts" in document ) {
        // Optimization for Repeat Views
        if( sessionStorage.fl ) {
            document.documentElement.className += " fl";
	    return;
        }

        // Load font
        Promise.all([
            document.fonts.load("1em myFont"),
            document.fonts.load("700 1em myFont")
        ]).then(()=>{
            document.documentElement.className += " fl";
            sessionStorage.fl = true
        });
    }
})();
</script>


De '2 stage render' oplossing

De 2 stage render oplossing is vooral geschikt wanneer je meerdere versies van hetzelfde font wilt gebruiken (4300,600,700,italic).

Bij deze methode, die verder borduurt op de 'Font with a class' techniek, laad je eerst een zo klein mogelijk font met alleen letters, nummers en interpunctie dat door middel van font-syntesis zo snel mogelijk een eerste versie van het font weergeeft. Ondertussen laad je de echte fonts en zodra deze zijn geladen voeg je een class to aan de pagina zodra deze fonts worden geactiveerd.

Deze techniek voorkomt layout-shifts die op zouden kunnen treden iedere keer dat een lettertype geladen wordt en door het preloaden van een klein font start de FOUT al heel vroeg tijdens het laden.

<link rel="preload" href="/myFontSubset400.woff2" 
      as="font" type="font/woff2" crossorigin>
<style>
@font-face {
    font-family: 'myFontSubset';
    font-weight:400;
    src: local('myfont'),url('/myFontSubset400.woff2') format('woff2');
}
@font-face {
    font-family: 'myFont';
    font-weight:400;
    src: local('myfont'),url('/myFont400.woff2') format('woff2');
}
@font-face {
    font-family: 'myFont';
    font-weight:700;
    src: local('myfont'),url('/myFont700.woff2') format('woff2');
}
body {
    // fallback
    font-family: sans-serif;
}
html.flsubset body{
    // web font
    font-family: 'myFontSubset';
}
html.fl body{
    // web font
    font-family: 'myFont';
}
</style>
<script>
(()=>{
    if( "fonts" in document ) {
        // Optimization for Repeat Views
        if( sessionStorage.fl ) {
            document.documentElement.className += " fl";
	    return;
        }

        document.fonts.load("1em myFontSubset").then(() => {
            document.documentElement.className += " flsubset";

            // Load font
            Promise.all([
                document.fonts.load("1em myFont"),
                document.fonts.load("700 1em myFont")
            ]).then(()=>{
                document.documentElement.className += " fl";
                sessionStorage.fl = true
            });
        });
    }
})();
</script>

Conclusie

Gebruik je web fonts? Dan is het zeker de moeite waard om 1 van de 3 geavanceerde technieken toe te passen. Het scheelt gemakkelijke enkele seconden laadtijd en het is relatief eenvoudig om dit te te passen. Heb je hulp nodig? Neem dan even contact op

Het online-marketing dashboard voor professionals

Meer dan de helft van de Emerce top-100 digital marketingbureaus gebruikt MarketingTracer.
Geen opzegtermijn, direct online, gratis trial.