Contenu principal

Optimiser le chargement des webfonts

lundi 31 juillet 2023

Nous voici de retour dans cette sĂ©rie d’articles liĂ©s Ă  la web performance. Cette semaine, je vous propose de se concentrer sur les polices d’écriture. C’est souvent un Ă©lĂ©ment essentiel de la charte graphique de votre site web. C’est aussi un Ă©lĂ©ment qui peut ruiner vos performances s’il n’est pas bien gĂ©rĂ©.

Nous allons donc voir dans cet article les diffĂ©rentes configurations possibles et ce qu’il est pertinent de gĂ©rer pour amĂ©liorer au maximum la performance ressentie par vos utilisateurices.

Si vous ressentez le besoin d’avoir plus d’accompagnement dans votre entreprise pour mettre en place ces sujets, n’hĂ©sitez pas Ă  me contacter par mail. Peut-ĂȘtre pouvons-nous travailler ensemble pour proposer la meilleure expĂ©rience Ă  vos utilisateurices ?

Récapitulatif des articles de cette série :

PrĂ©requis : cet article se concentre sur l’optimisations et les points d’attention sur la gestion des fonts. Si vous n’avez jamais installĂ© de web font ou que vous n’avez jamais vu de @font-face jusqu’à maintenant, je vous conseille de commencer par suivre ce tutoriel utiliser une web font (EN).

Avez-vous vraiment besoin d’une font ?

Souvent, la premiĂšre considĂ©ration Ă  avoir au sujet des performances est de savoir s’il est possible de faire moins. Est-ce que la police que vous utilisez est indispensable ?

Vous pourriez par exemple envisager d’utiliser cette rĂšgle par dĂ©faut :

font-family: system-ui, sans-serif;

Source: Modern Font Stacks (d’autres styles disponibles)

C’est une façon de dire au site web : utilise juste la police par dĂ©faut du systĂšme. Ainsi, l’utilisateurice ne devrait pas ĂȘtre trop choqué·e par celle-ci puisque c’est celle qu’iel voit au quotidien dans son OS. Ce blog a longtemps utilisĂ© cette mĂ©thode. Mais ça ne reprĂ©sente que rarement la rĂ©alitĂ© d’un site web d’entreprise.

Bien souvent vous n’aurez pas en main les clĂ©s pour prendre cette dĂ©cision. Il s’agit plutĂŽt d’une considĂ©ration de marque : la police d’écriture est un moyen efficace de vĂ©hiculer une Ă©motion et donc de se positionner.

Graphique représentant le nombre de sites web utilisant une font custom. La montée en puissance commence en 2012 et nous sommes en train d'arriver à un plateau autour des 84% en 2022
84% des sites web utilisent une web font en 2022 (Source: Web Almanac 2022)

Mais peut-ĂȘtre pouvez-vous faire pencher la balancer vers moins de fonts. Quand un·e designer·euse me demande combien de fonts utiliser pour le site, je rĂ©ponds gĂ©nĂ©ralement 2 fonts avec un grand maximum de 3, graisses incluses. Par exemple, sur ce blog, c’est la font “Assistant” qui utilise 3 graisses diffĂ©rentes (font-weight). Mais si j’avais eu une font pour les titres et une font pour les paragraphes, j’aurais poussĂ© pour que les titres aient toujours la mĂȘme graisse.

Rappels sur les bonnes pratiques réseau

Avant de rentrer dans le vif du sujet, je veux juste rappeler en quelques mots 2 bonnes pratiques que nous avions vu dans un des premiers articles de cette sĂ©rie Comment analyser la trace rĂ©seau de votre site ? :

  • assurez vous que la font est hĂ©bergĂ©e sur le mĂȘme domaine que votre page HTML :
    Sur une connexion classique, cela vous fera gagner facilement 100ms de temps de chargement. Mais sur des connexions plus ératiques, vous pouvez facilement considérer que cela vous fera gagner dans les 500ms.

  • Ă©vitez les effets de cascade en ajoutant un <link rel="preload">
    Classiquement, c’est le CSS qui indique Ă  votre navigateur qu’il faut tĂ©lĂ©charger une nouvelle font. Pour gagner en temps d’attente, vous pouvez l’indiquer dans votre HTML afin que la font soit tĂ©lĂ©chargĂ©e en parallĂšle de votre CSS.

    <head>
    	<link
    		rel="preload"
    		as="font"
    		type="font/woff2"
    		href="/assets/fonts/ovo.woff2"
    		crossorigin
    	/>
    </head>
    

Ces premiĂšres bonnes pratiques pourront vous amener dans la bonne direction. Mais elles ne seront pas suffisantes.

Comment faire alors pour charger rapidement les fonts ?

Comme Ă  chaque fois en informatique, nous allons voir qu’il n’y a pas une maniĂšre unique de faire les choses. Il vous faudra donc choisir la bonne mĂ©thode en fonction de votre situation. Du plus simple au plus compliquĂ© nous verrons donc :

Vous pouvez bien Ă©videmment aller directement aux sections qui vous intĂ©ressent. Sachez cependant que je rentre dans pas mal de dĂ©tails lors de la premiĂšre section. Si les notions de subset, de font-display ou de size-adjust vous sont Ă©trangĂšres ou ont besoin de rappels, n’hĂ©sitez donc pas Ă  prendre le temps de la lire avant de passer aux suivantes.

Par ailleurs, si vous voulez un historique de comment on en est arrivĂ© lĂ , sachez que cet article par Zach Leatherman retrace les diffĂ©rentes techniques qui existent. Mon but ici est de vous proposer les mĂ©thodes qui vous seront utiles aujourd’hui avec le support des navigateurs qui s’est grandement amĂ©liorĂ©.

1. Utiliser une font unique

Admettons que la seule font que vous souhaitez utiliser est Ovo.

Screenshot de la police Ovo avec le texte : Considérant que la reconnaissance de la dignité
La police Ovo appliquée à un court extrait de texte

1.1. Récupérer le fichier de font

La premiĂšre Ă©tape va ĂȘtre de rĂ©cupĂ©rer la font et de la transformer dans le bon format. En effet, si vous tĂ©lĂ©charger la police directement depuis Google Font, vous la rĂ©cupĂ©rerez en TTF. Mais :

  • le format le plus performant est WOFF 2.0 qui est supportĂ© par 96.4% des navigateurs aujourd’hui et peut vous faire Ă©conomiser 67% de taille de fichier (48KB -> 16KB)
  • il y a des chances que vous n’utilisiez qu’un nombre rĂ©duit de caractĂšres. Par exemple si votre site est en français, vous utiliserez â€œĂ©â€ et “Ɠ”, mais pas “ß” par exemple. Cela nous fait gagner quelques KB de plus (16KB -> 12KB).

Pour appliquer ces changements, vous pouvez exécuter la commande suivante :

pyftsubset \
    ./static/fonts/Ovo-Regular.ttf \
    --unicodes="U+20-5F, U+61-7A, U+7C, U+A0, U+A7, U+A9, U+AB, U+B2-B3, U+BB, U+C0, U+C2, U+C6-CB, U+CE-CF, U+D4, U+D9, U+DB-DC, U+E0, U+E2, U+E6-EB, U+EE-EF, U+F4, U+F9, U+FB-FC, U+FF, U+152-153, U+178, U+2B3, U+2E2, U+1D48-1D49, U+2010-2011, U+2013-2014, U+2019, U+201C-201D, U+2020-2021, U+2026, U+202F-2030, U+20AC, U+2212" \
    --layout-features='*' \
    --flavor=woff2 \
    --output-file=./static/fonts/Ovo-critical.woff2

💡 Pour installer pyftsubset, vous pouvez exĂ©cuter pip3 install fonttools[woff,unicode].

Si vous n’avez pas python sur votre machine, vous pouvez utiliser le petit Dockerfile que j’ai moi-mĂȘme utiliser pour ce blog.

Cette commande va supprimer tous les caractĂšres qui ne sont pas dans la liste --unicodes. Vous pouvez dĂ©terminer cette liste en utilisant Character Table qui indique par langue quels caractĂšres utiliser. Celle que j’ai utilisĂ© dans l’exemple ci-dessus est pour le Français. Comme vous pouvez le voir Ă  la fin du tableau, certains caractĂšres ne font pas forcĂ©ment partie de notre quotidien : †, ‡. N’hĂ©sitez donc pas Ă  faire le tri par vous mĂȘme.

Si vous voulez une liste trÚs précise, vous pouvez récupérer la liste de votre site directement en utilisant glyphhanger. En CLI, il peut parcourir votre site pour en lister les caractÚres utiliser, se concentrer sur des sections critiques, etc.

Pour les autres options telles que --layout-features, n’hĂ©sitez pas Ă  vous rĂ©fĂ©rer Ă  l’article complet de Code Colibri.

💡 Quand vous rĂ©cupĂ©rez les polices depuis Google Font, vous pourrez tomber sur des subset tout prĂȘts : latin, latin-ext, cyrillic, etc. GĂ©nĂ©ralement, latin est la version la plus proche de ce que vous voulez si vous opĂ©rez principalement en occident. Mais gardez en tĂȘte qu’il y a quand mĂȘme plus de caractĂšres que nĂ©cessaire et donc qu’il est pertinent de construire vous mĂȘme votre liste de caractĂšres (--unicodes).

1.2 Référencer la font dans notre CSS

Maintenant que nous avons notre fichier optimisĂ©, nous allons pouvoir indiquer au navigateur qu’une web font est disponible en CSS via @font-face :

@font-face {
	font-family: 'Ovo';
	font-display: fallback;
	/* Penser Ă  bien hĂ©berger la font sur le mĂȘme domaine que mon site */
	src: url(/fonts/ovo.woff2) format('woff2');
	/* RĂ©utiliser ici exactement la mĂȘme liste unicode que ci-dessus */
	unicode-range: U+20-5F, U+61-7A, U+7C, U+A0, U+A7, U+A9, U+AB, U+B2-B3, U+BB, U+C0, U+C2, U+C6-CB,
		U+CE-CF, U+D4, U+D9, U+DB-DC, U+E0, U+E2, U+E6-EB, U+EE-EF, U+F4, U+F9, U+FB-FC, U+FF,
		U+152-153, U+178, U+2B3, U+2E2, U+1D48-1D49, U+2010-2011, U+2013-2014, U+2019, U+201C-201D,
		U+2020-2021, U+2026, U+202F-2030, U+20AC, U+2212;
}

body {
	font-family: 'Ovo', serif;
}

Plusieurs choses intéressantes à noter ici.

Je n’ai indiquĂ© ni font-style ni font-weight dans @font-face. Ainsi, le navigateur utilise la font Ovo quand bien mĂȘme le fichier qu’on a utilisĂ© ne fournit qu’un style roman (font-style: normal; font-weight: normal). En effet, quand le navigateur va tomber sur du gras et/ou de l’italique, il va alors faire de la Font Synthesis : il va inventer une graisse et/ou un style Ă  partir du style roman. Ce ne sera pas parfait, mais ce sera souvent mieux que d’utiliser une typo complĂštement diffĂ©rente pour le gras/italique. Selon la font choisie, le rĂ©sultat pourra ĂȘtre dĂ©cevant, voire rĂ©dhibitoire. Dans ce cas lĂ  vous devrez passer par une Variable Font ou plusieurs fonts.

J’utilise la rùgle font-display: fallback. Cette ligne indique comment doit se comportement le navigateur pendant le chargement de la font. Notamment, comment est-ce que la font doit s’afficher si le HTML et le CSS ont fini de se charger mais que le navigateur n’a pas encore reçu le fichier ovo.woff2 ?

  • si cela ne fait pas longtemps (< ~100ms), alors la typo n’est pas affichĂ©e (Flash Of Invisible Text (FOIT))
  • si cela fait un peu longtemps (< ~3s), alors la premiĂšre typo disponible dans la font-family s’affiche (Flash Of Unstyled Text (FOUT)).
  • si cela fait longtemps (> ~3s), alors on abandonne le chargement de ovo.woff2 et on reste sur la typo de l’étape prĂ©cĂ©dente.

C’est un bon comportement par dĂ©faut parce que :

  • Si la font est rapide Ă  charger, on n’a pas de saut de texte au chargement des fonts
  • Si la font met du temps Ă  se charger, vos utilisateurices ne restent pas bloqué·e·s sur une page vide
  • Si la font est vraiment trop lente, les personnes auront certainement commencĂ© Ă  lire le texte et seront au milieu de la page : pour Ă©viter de perturber leur lecture, on Ă©vite de changer de font.

D’autres mĂ©thodes existent dans font-display. Je n’entrerai pas dans le dĂ©tail, mais si vous dĂ©cidez de choisir autre chose que fallback, privilĂ©giez soit optional soit swap. Jamais auto ou block qui auront un effet dĂ©sastreux sur les connexions lentes.

1.3 Choisir des bons fallbacks

Dans la dĂ©finition de la font-family (font-family: 'Ovo', serif;), vous pouvez constater que j’ai mis serif aprĂšs Ovo. C’est une Ă©tape importante dans la dĂ©finition de la font parce que vos utilisateurices verront cette font Ă  un moment ou Ă  un autre (dans un train, pendant leur voyage Ă  la campagne ou juste parce que leur wifi a dĂ©cidĂ© d’ĂȘtre particuliĂšrement lent).

Pourquoi serif ? Pour avoir un style proche de la typo finale, tout en Ă©tant sĂ»r qu’elle soit disponible. serif est adaptĂ© si la police finale a des petits pieds au bout des lettres (= empattements). Sinon, privilĂ©giez plutĂŽt sans-serif.

La lettre T écrite en serif et en sans serif. La différence entre les deux se voit au niveau des bouts des branches de la lettre.
sans-serif / serif (Source)

Mais on peut mieux faire.

Size-adjust

Notamment, une nouvelle mĂ©thode est en train d’arriver dans les navigateurs : size-adjust. Celle-ci est disponible sur 73% des navigateurs actuellement, on croise les doigts pour que ça arrive aussi sur Safari.

Ce size-adjust a pour but d’ajuster la font de fallback pour qu’elle se rapproche au maximum des espacements utilisĂ©s par la font finale. Ainsi, si la font de fallback est affichĂ©e puis remplacĂ©e par la font finale, on minimise les soucis de lecture.

Dans l’exemple ci-dessous, vous pouvez changer de tabs pour constater la diffĂ©rence que cela provoque :

  • Si vous passez de “Fallback sans size-adjust” Ă  “Font finale”, vous constaterez que vous aurez du mal Ă  vous repĂ©rer : il vous faudra un petit temps avant de pouvoir reprendre votre lecture
  • Si vous passez de “Fallback avec size-adjust” Ă  “Font finale”, vous remarquerez un changement de graisse et de style, mais la diffĂ©rence est minime
Fallback sans size-adjust

Pour mettre en place ce size-adjust, vous aurez besoin de deux parties :

  1. déclarer votre font de fallback

    @font-face {
    	font-family: 'Ovo-fallback';
    	size-adjust: 109.1%;
    	ascent-override: 72%;
    	src: local('Times New Roman');
    	unicode-range: U+20-5F, U+61-7A, U+7C, U+A0, U+A7, U+A9, U+AB, U+B2-B3, U+BB, U+C0, U+C2,
    		U+C6-CB, U+CE-CF, U+D4, U+D9, U+DB-DC, U+E0, U+E2, U+E6-EB, U+EE-EF, U+F4, U+F9, U+FB-FC,
    		U+FF, U+152-153, U+178, U+2B3, U+2E2, U+1D48-1D49, U+2010-2011, U+2013-2014, U+2019,
    		U+201C-201D, U+2020-2021, U+2026, U+202F-2030, U+20AC, U+2212;
    }
    
  2. utiliser ce fallback dans votre déclaration de font-family

    body {
    	font-family: 'Ovo', 'Ovo-fallback', serif;
    }
    

Ces quelques lignes fonctionneront pour Ovo, mais je pense qu’il y a quelques subtilitĂ©s Ă  expliquer :

  • Pourquoi Times New Roman plutĂŽt que serif ? Parce que chaque size-adjust va dĂ©pendre de la typo utilisĂ©e. Si vous avez utilisĂ© serif, chaque OS aurait eu une font diffĂ©rente et donc vous n’auriez pas pu adopter un size-adjust unique. Par ailleurs, Times New Roman a l’avantage d’ĂȘtre disponible sur beaucoup d’OS. Si vous cherchez sa contrepartie en serif, alors Arial est certainement le meilleur choix. (Safe web fonts)

    A noter toutefois que Times New Roman et Arial ne sont pas rĂ©ellement disponibles partout, contrairement Ă  ce qu’on trouve sur la majoritĂ© des sites qui en parlent. Notamment, elles le seront certainement sur Windows, Mac et Ubuntu. Mais sur mon Manjaro, elles n’y sont pas. Si vous ĂȘtes jusqu’au-boutiste, il peut ĂȘtre intĂ©ressant d’ajouter un deuxiĂšme fallback avec les fonts DejaVu Serif ou DejaVu Sans qui, elles, sont libres de droit et donc largement disponibles dans l’écosystĂšme Linux.

    Vous ne pourrez toutefois jamais ĂȘtre certain que vos font de fallback sont disponibles. Il est donc important de garder la notion de serif ou sans-serif dans la font-family.

  • Je vous ai parlĂ© de size-adjust, mais un ascent-override a fait son apparition. La diffĂ©rence entre les deux est que size-adjust va gĂ©rer l’alignement horizontal tandis que ascent-override s’occupe de l’alignement vertical.

    Comment choisir leurs valeurs ? La rĂ©ponse la plus simple est avec cet outil qui peut bien vous macher le travail : Automatic font matching. Le seul souci c’est qu’il part du principe que Arial ou Times New Roman est installĂ© sur votre PC. Si ce n’est pas le cas, il faudra faire la manipulation manuellement en tatonnant jusqu’à trouver la bonne valeur.

  • Pourquoi dĂ©finir une unicode-range sur le fallback ? Vous pouvez constater que j’ai conservĂ© exactement la mĂȘme unicode-range que pour la font finale. Le but ici est de s’assurer que la font de fallback ne fasse pas trop de zĂšle. Notamment certaines fonts contiennent des emojis noir et blancs. Si vous oubliez de mettre l’unicode-range, vous retrouvez donc avec un emoji en noir et blanc plutĂŽt que l’emoji habituellement affichĂ© par votre navigateur. En renseignant la bonne unicode-range, vous vous assurez que les emojis ne soient pas considĂ©rĂ©s par cette @font-face. Pour en savoir, cet excellent article rĂ©sume bien l’enfer des emojis (EN) 😁

  • Enfin, que se passe-t-il si ma font a plusieurs graisses ? En effet, quand on parle de size-adjust on parle d’alignement horizontal. Et la graisse impacte l’espacement horizontal. Vous allez donc avoir besoin d’un size-adjust diffĂ©rent. L’idĂ©e est alors d’ajouter une @font-face diffĂ©rente spĂ©cialement conçue pour cette graisse en spĂ©cifiant font-weight: 700 :

    @font-face {
    	font-family: 'Ovo-fallback';
    	font-weight: 700;
    	/* Adapter la valeur du size-adjust Ă  la font */
    	size-adjust: 104.1%;
    	ascent-override: 72%;
    	/* Choisir la bonne graisse sur la font de fallback */
    	src: local('Times New Roman Bold');
    	unicode-range: U+20-5F, U+61-7A, U+7C, U+A0, U+A7, U+A9, U+AB, U+B2-B3, U+BB, U+C0, U+C2,
    		U+C6-CB, U+CE-CF, U+D4, U+D9, U+DB-DC, U+E0, U+E2, U+E6-EB, U+EE-EF, U+F4, U+F9, U+FB-FC,
    		U+FF, U+152-153, U+178, U+2B3, U+2E2, U+1D48-1D49, U+2010-2011, U+2013-2014, U+2019,
    		U+201C-201D, U+2020-2021, U+2026, U+202F-2030, U+20AC, U+2212;
    }
    
Que faire quand size-adjust n’est pas disponible ?

Je disais un peu plus haut que seulement 73% des navigateurs supportent size-adjust aujourd’hui. Que faire pour les 27% restants ?

Tout d’abord vous pouvez considĂ©rez que c’est du progressive enhancement : alors vous pouvez directement passer Ă  la partie suivante sur les Variable Fonts

Sinon, il vous faudra passer par du JavaScript. En effet, ce que nous avons vu, c’est que size-adjust et ascent-override permettent de gĂ©rer les espacements horizontaux et verticaux de la font. Mais quand on y pense, c’est aussi ce que font letter-spacing et line-height en CSS.

La seule diffĂ©rence est que dans le premier cas vous pouvez le dĂ©finir au niveau d’une font unique, et dans l’autre vous ĂȘtes obligĂ© de dĂ©finir le mĂȘme pour toutes les fonts (fallback et finale).

L’astuce est alors d’utiliser des classes CSS qui vous permettent de savoir quelle font est en cours d’utilisation :

body {
    font-family: 'Times New Roman', serif;
	letter-spacing: 0.05em;
	line-height: 1.45;
}

.font-loaded body {
    font-family: Ovo, serif,
	letter-spacing: 0;
	line-height: 1.5;
}

C’est une reprĂ©sentation trĂšs simplifiĂ©e de la rĂ©alitĂ©. Vous aurez sĂ»rement besoin de l’adapter pour chaque selecteur CSS qui Ă  l’habitude de manipuler ces propriĂ©tĂ©s (ex : h1, h2, h3, h4, strong, etc.).

Pour obtenir cette nouvelle classe font-loaded, nous devons passer au JavaScript en utilisant FontFaceObserver :

/* Utiliser le mĂȘme nom que celui utilisĂ© dans la propriĂ©tĂ© `font-family` */
const ovoFont = new FontFaceObserver('Ovo');
ovoFont.load().then(() => {
	document.documentElement.classList.add('font-loaded');
});

Ainsi, dÚs que la font Ovo a fini de se charger la classe font-loaded est ajoutée.

Ce JavaScript doit ĂȘtre ajoutĂ© de maniĂšre synchrone Ă  l’aide d’une balise <script> inline dans le <head> de votre site si vous voulez Ă©viter au maximum un Flash Of Unstyled Text. MĂȘme si la librairie en elle mĂȘme est lĂ©gĂšre (1.3KB gzip), ça reprĂ©sente une lĂ©gĂšre perte de performance et un sacrĂ© effort de maintenabilitĂ© en plus pour penser Ă  maintenir tous les letter-spacing & line-height, avec et sans fallback dans votre CSS.

Par ailleurs, il est impossible d’utiliser FontFaceObserver uniquement pour les navigateurs qui ne supportent pas size-adjust. Si vous souhaitez gĂ©rer les 27% de navigateurs qui ne gĂšrent pas size-adjust, vous devrez donc tout le temps passer par FontFaceObserver et subir la lĂ©gĂšre perte de performance et de maintenabilitĂ© que cela reprĂ©sente. La question qu’il vous reste Ă  rĂ©soudre est donc de savoir si cela vaut le coup ou non.

2. Utiliser plusieurs graisses de fonts grĂące aux Variable Fonts

Nous avons donc vu jusqu’à maintenant comment charger une unique font custom. La consĂ©quence de ce choix est que si nous avons du gras dans la page, alors le navigateur utilisera de la Font Synthesis : il simulera une graisse en la devinant Ă  partir de la font normale.

Parfois c’est suffisant, mais parfois
 c’est moche.

C’est lĂ  que les variable fonts peuvent ĂȘtre un bon moyen de prĂ©server la simplicitĂ© de chargement et la lĂ©gertĂ© de vos fichiers tout en mettant Ă  disposition plusieurs variantes de graisse. De maniĂšre simplifiĂ©e, ça fonctionne en enregistrant dans le fichier les glyphes fins ET les glyphes gras, puis en faisant une interpolation entre les deux pour obtenir la graisse choisie.

Vous en trouverez notamment un bon nombre sur Google Fonts si vous cochez la case “Show only variable fonts”. Il y a mĂȘme des trucs rigolos avec des fonts colorĂ©es avec des effets 3D.

Si nous revenons Ă  notre code, quelle diffĂ©rence par rapport Ă  la font unique ?

Dans le cas de mon blog, j’ai choisi la font Assistant. Celle-ci a des graisses allant de 200 Ă  800. La seule diffĂ©rence se trouve au niveau de l’appel Ă  @font-face avec l’ajout de la propriĂ©tĂ© font-weight :

@font-face {
	font-family: 'Assistant';
	/* J'indique au navigateur que la font est capable
    de gérer des graisses allant de 200 à 800 */
	font-weight: 200 800;
	font-display: fallback;
	src: url(/fonts/Assistant.woff2) format('woff2');
	unicode-range: U+20-5F, U+61-7A, U+7C, U+A0, U+A7, U+A9, U+AB, U+B2-B3, U+BB, U+C0, U+C2, U+C6-CB,
		U+CE-CF, U+D4, U+D9, U+DB-DC, U+E0, U+E2, U+E6-EB, U+EE-EF, U+F4, U+F9, U+FB-FC, U+FF,
		U+152-153, U+178, U+2B3, U+2E2, U+1D48-1D49, U+2010-2011, U+2013-2014, U+2019, U+201C-201D,
		U+2020-2021, U+2026, U+202F-2030, U+20AC, U+2212;
}

đŸ€” A noter que vous verrez d’autres tutos & documentations la possibilitĂ© de changer le format pour indiquer au navigateur que c’est une Font Variable.

@font-face {
	src: url(/fonts/Assistant.woff2) format('woff2') tech('variations'),
	     url(/fonts/Assistant.woff2) format('woff2-variations');
}

Malheureusement chez moi ces syntaxes dĂ©sactivent complĂštement les fonts. Pourtant, en restant sur la version simplifiĂ©e tout rentre dans l’ordre. Je n’ai pas rĂ©ussi Ă  trouver d’informations Ă  ce sujet. Etant donnĂ© le support trĂšs large des variable fonts je pars donc du principe que, au pire, ça tĂ©lĂ©chargera la font dans tous les cas et fallbackera si finalement la font ne fonctionne pas sur le navigateur.

Cette notion de font-weight est la seule diffĂ©rence comparĂ©e au chargement d’une font unique. Toutes les autres problĂ©matiques restent identiques. Pensez donc bien à :

  1. Optimiser le fichier de font avec pyftsubset
  2. Utiliser les bonnes rĂšgles de font-display
  3. GĂ©rer les fallbacks et bien faire attention Ă  l’ajustement des fallbacks en fonction des diffĂ©rentes graisses

Pour un exemple complet, vous pouvez vous référer à la feuille de style de mon blog.

3. Utiliser plusieurs fonts

Dernier cas de figure : votre site est complexe, repose sur plusieurs fonts ou a minima une font qui a plusieurs graisses sans Font Variable disponible. Comment combiner tout ce qu’on a vu jusqu’à maintenant pour proposer la meilleure solution à l’utilisateur ?

Dans les exemples ci-dessous, j’utiliserai Montserrat pour les titres et Cabin pour les paragraphes. En rĂ©alitĂ© celles-ci existent sous form de Font Variables, mais pour mieux illustrer la situation, je considĂ©rerai que non.

3.1. Définir une @font-face par typo

Si on exclut les @font-face de fallback, nous avions eu besoin d’une unique @font-face jusqu’à maintenant. Si nous avons plusieurs fonts, il va donc falloir duppliquer ces dĂ©clarations pour chacunes d’elles :

@font-face {
	font-family: 'Montserrat';
	font-weight: 400;
	src: url('/fonts/Montserrat.woff2') format('woff2');
}

@font-face {
	font-family: 'Montserrat';
	font-weight: 700;
	src: url('/fonts/Montserrat-Bold.woff2') format('woff2');
}

@font-face {
	font-family: 'Cabin';
	font-weight: 400;
	src: url('/fonts/Cabin.woff2') format('woff2');
}

@font-face {
	font-family: 'Cabin';
	font-weight: 700;
	src: url('/fonts/Cabin-Bold.woff2') format('woff2');
}

Mais cela apporte son lot de complexités :

  • Plus de fichiers, donc plus long Ă  tĂ©lĂ©charger : il est d’autant plus important de bien optimiser et prioriser vos ressources afin que votre site ait fini de s’afficher au plus vite.
  • Chaque font arrive Ă  son propre rythme : Admettons que vous receviez la font Cabin, puis, 500ms plus tard, Montserrat arrive pour les titres. A chaque fin de chargement le navigateur va changer le contenu et l’adapter Ă  la nouvelle font. Au delĂ  de l’effet sapin de NoĂ«l, le navigateur va ĂȘtre obligĂ© de recalculer l’ensemble des positions du site (c’est l’étape de Layout) Ă  chaque font chargĂ©e. Cette Ă©tape peut ĂȘtre trĂšs couteuse si vous avez un site complexe (ex : > 1500 Ă©lĂ©ments de DOM) ou qui utilise des systĂšmes de layout particuliĂšrement complexes (ex : beaucoup de flexbox imbriquĂ©es, etc.).

Pour la lenteur de chargement, il s’agit essentiellement de bien optimiser les ressources pour limiter les impacts :

  • les fonts sont hĂ©bergĂ©es sur le mĂȘme domaine que votre site
  • chaque font a Ă©tĂ© optimisĂ©e grĂące Ă  pyftsubset
  • chaque @font-face a le fallback qui va bien en utilisant size-adjust

Cependant, pour les problĂšmes de rythme, c’est plus compliquĂ©. En effet, il n’existe pas de moyen pour indiquer Ă  votre navigateur de ne pas afficher une font tant que l’autre n’est pas chargĂ©e. La solution serait donc d’orchestrer vos chargements de cette façon :

body {
	font-family: Cabin-critical, Cabin-fallback, sans-serif;
}

h1,
h2,
h3,
h4,
h5,
blockquote {
	font-family: Cabin-critical, Montserrat-fallback, sans-serif;
}

.fonts-loaded body {
	font-family: Cabin, Cabin-fallback, sans-serif;
}

.fonts-loaded h1,
.fonts-loaded h2,
.fonts-loaded h3,
.fonts-loaded h4,
.fonts-loaded h5,
.fonts-loaded blockquote {
	font-family: Montserrat, sans-serif;
}
  1. Afficher une font de fallback en utilisant une de celles dĂ©jĂ  installĂ©es sur l’ordinateur de votre utilisateurice (ici sans-serif).

  2. Afficher ensuite la font la plus impactante en l’optimisant agressivement.

    La font la plus impactante sera généralement celle pour les paragraphes (ici Cabin). Mais si votre site fonctionne essentiellement avec des splashscreens faits uniquement des titres, alors privilégiez celle de vos titres (ici Montserrat).

    Vous pouvez l’optimiser agressivement en rĂ©duisant encore plus le nombre de glyphes Ă  disposition. Peut-ĂȘtre pouvez vous vous contenter d’un espace, de 0-9, a-z et A-Z ? Si oui, ce serait les charactĂšres unicodes suivants : U+20,U+30-39,U+41-51,U+61-7A. C’est plus facile cela dit en anglais qu’en français oĂč ça risque de faire lĂ©gĂšrement bizarre pour les accents.

    pyftsubset \
         ./static/fonts/Cabin-Regular.ttf \
         --unicodes="U+20,U+30-39,U+41-51,U+61-7A" \
         --layout-features='*' \
         --flavor=woff2 \
         --output-file=./static/fonts/Cabin-Critical.woff2
    

    L’intĂ©rĂȘt de cette Ă©tape est que le site respecte les tons typographiques de votre charte trĂšs rapidement, mĂȘme si toutes les fonts n’ont pas rĂ©ussi Ă  charger.

    Par ailleurs dans le cas oĂč vous n’avez qu’un seul type de fonts (ex: uniquement Montserrat), mais plusieurs graisses Ă  charger, cela vous permet de vous reposer sur la Font Synthesis qui sera vraisemblablement assez proche des graisses finales que vous comptez charger.

    Etant donnĂ© que c’est la seule font critique au premier affichage, c’est sur ce fichier lĂ  que vous pouvez ajouter un <link rel="preload"> dans l’entĂȘte de votre site.

    <head>
    	<link
    		rel="preload"
    		as="font"
    		type="font/woff2"
    		href="/fonts/Cabin-Critical.woff2"
    		crossorigin
    	/>
    </head>
    
  3. Charger toutes les autres fonts et attendre que la totalitĂ© soit tĂ©lĂ©chargĂ©e avant d’ajouter la classe fonts-loaded Ă  votre document.

    Pour faire cela, vous pouvez utiliser la mĂȘme technique que pour les navigateurs qui ne supportent pas size-adjust : en passant par FontFaceObserver

    const cabinFont = new FontFaceObserver('Cabin');
    const cabinBoldFont = new FontFaceObserver('Cabin', {
      { weight: 700 }
    });
    const montserratFont = new FontFaceObserver('Montserrat');
    const montserratBoldFont = new FontFaceObserver('Montserrat', {
      { weight: 700 }
    });
    
    Promise.all([
      cabinFont.load(),
      cabinBoldFont.load(),
      montserratFont.load(),
      montserratBoldFont.load(),
    ]).then(() => {
    	 document.documentElement.classList.add('font-loaded');
    });
    

    Ainsi, grĂące au Promise.all, on attend que toutes les fonts soient tĂ©lĂ©chargĂ©es avant de les afficher. Le temps que l’on perd Ă  attendre l’arrivĂ©e des fonts, on va le gagner en performance ressentie et en temps de calcul pour le navigateur.

    Attention toutefois à bien penser à rendre ce script inline et synchrone dans le <head> de votre page. Ainsi, si les fonts sont déjà en cache, le navigateur sera en mesure de les afficher directement.

Récapitulatif

Nous voilĂ  arrivĂ© au bout. FĂ©licitations ! 👏

Si on rĂ©capitule un peu tout ce qu’on vient de voir, voici quelques Ă©lĂ©ments que je veux que vous gardiez en tĂȘte :

  • commencez par faire le bilan sur vos fonts : lesquelles sont utiles, lesquelles peuvent ĂȘtre Ă©vitĂ©es ?
    • est-ce qu’il est possible d’utiliser des fonts variables ?
  • Faites en sorte que vos fonts soient les moins bloquantes possible :
    • les hĂ©berger sur le mĂȘme nom de domaine que votre site
    • veiller Ă  ce qu’elles soient en cache pour ne pas avoir Ă  les retĂ©lĂ©charger Ă  la prochaine page
    • ajouter des preload sur les fonts critiques
    • optimiser les fonts pour n’avoir que les caractĂšres nĂ©cessaires (pyftsubset)
    • utiliser font-display: fallback ou swap pour Ă©viter les pages blanches sur connexions lentes
  • Travaillez sur les fonts de fallback pour limiter les effets sapin de NoĂ«l :
    • en utilisant size-adjust sur les fonts de fallback
    • en utilisant FontFaceObserver pour grouper les mises Ă  jour en une seule

Pfiou ! Ca en fait des choses. Vous en voyez d’autres ? En tout cas, avec ça vous devriez voir un joli boost de performance sur votre site si ce n’était pas dĂ©jĂ  en place. N’hĂ©sitez pas Ă  me partager vos avancĂ©es Mastodon ou Twitter.

A la semaine prochaine pour la suite ! Je me concentrerai cette fois sur les icones. 👀


Si vous voulez suivre mes publications, il paraĂźt que j'ai un feed RSS, Mastodon et un Twitter.

Si vous pensez à d'autres méthodes que vous voudriez que je mette en place (pigeon voyageur, avion en papier, etc.), n'hésitez pas à me les proposer :)