FlowingDev

@font-face, expliqué : apprendre aux navigateurs à parler votre typographie

Maîtrisez la règle CSS @font-face pour diffuser efficacement des polices web personnalisées à l'aide de font-display, du preloading, des unicode-ranges et des graisses variables.

Essayer l'outil: Générateur @font-face

En une phrase

@font-face est la règle CSS qui permet à un navigateur web de télécharger et d'afficher une typographie sur mesure, libérant enfin les designers de la tyrannie de la demi-douzaine de polices "web-safe" préinstallées sur chaque ordinateur.

Le problème que ça résout

Si vous créiez des sites web à la fin des années 90 ou au début des années 2000, la typographie web était un paysage d'une tristesse absolue. Vous étiez à la merci des polices qui traînaient sur le système d'exploitation de l'utilisateur. Vous écriviez font-family: Arial, Helvetica, sans-serif; et vous croisiez les doigts. Si un designer vous passait une maquette Photoshop avec une magnifique police serif sur mesure pour les titres, vous aviez trois terribles options :

  1. La bidouille de l'image (The Image Hack) : Découper le titre dans Photoshop et le servir dans une balise <img>. Le SEO détestait ça. Les lecteurs d'écran n'y voyaient rien. Les écrans Retina en feraient plus tard de la bouillie de pixels.
  2. sIFR (Scalable Inman Flash Replacement) : Un hack démoniaque qui utilisait du JavaScript pour trouver le texte, le cacher, et embarquer une minuscule animation Adobe Flash pour rendre exactement le même texte avec une police personnalisée. C'était lourd, buggé, et ça faisait fondre les batteries de PC portables.
  3. Cufón : Une librairie JavaScript qui convertissait les tracés de polices en graphiques vectoriels et les rendait sur un <canvas>. Magnifique, mais impossible de sélectionner ou de copier le texte facilement.

Le W3C avait en fait proposé @font-face dès CSS2 (1998), mais les navigateurs n'arrivaient pas à se mettre d'accord sur les formats de police, et les fonderies étaient terrorisées par le piratage (si vous mettiez un fichier .ttf sur un serveur, n'importe qui pouvait le télécharger).

Il aura fallu attendre les années 2010 et l'adoption massive du Web Open Font Format (WOFF) — un format spécifiquement compressé et taillé pour le web — pour que @font-face devienne une réalité pratique. Aujourd'hui, @font-face ne se contente pas de charger des polices ; il orchestre un ballet complexe de requêtes réseau, de cache, de subsetting (sous-ensembles) et de stratégies de rendu pour que votre site reste ultra-rapide tout en ayant exactement l'apparence voulue par le designer.

Comment ça marche sous le capot

À la base, @font-face est un ensemble d'instructions. Ça fait correspondre un nom que vous inventez avec un fichier qui traîne sur un serveur.

L'anatomie de la règle

Une déclaration @font-face moderne et prête pour la prod ressemble à ça :

@font-face {
  font-family: 'MyAwesomeFont';
  src: local('MyAwesomeFont Regular'), 
       url('/fonts/myawesomefont.woff2') format('woff2');
  font-weight: 400;
  font-style: normal;
  font-display: swap;
  unicode-range: U+0000-00FF;
}

Décortiquons la magie qui se cache dans ce bloc.

Formats et le monopole du WOFF2

Remarquez le format('woff2'). Pendant l'âge sombre, les développeurs devaient fournir une pile "bulletproof" massive de .eot (pour l'antique Internet Explorer), .ttf (pour les vieux Safari/Android), .svg (pour les vieux iOS), et .woff.

Aujourd'hui, le web tourne sous WOFF2. Il utilise la compression Brotli, ce qui permet d'obtenir des fichiers environ 30 % plus légers que son prédécesseur, WOFF. Comme WOFF2 bénéficie d'un support quasi universel des navigateurs (plus de 97 % dans le monde), il vous suffit généralement de servir un seul fichier .woff2, avec peut-être un fallback WOFF standard si vous ciblez de très vieilles machines d'entreprise.

Contrôler le flash (font-display)

Quand un navigateur tombe sur du texte stylisé avec une police custom, mais que le fichier de police n'a pas fini de télécharger, il doit faire un choix. Est-ce qu'il affiche le texte dans une police de fallback ? Ou est-ce qu'il cache le texte en attendant que la police custom arrive ?

Cela crée deux acronymes tristement célèbres : FOUT (Flash of Unstyled Text) et FOIT (Flash of Invisible Text). Le descripteur font-display vous permet de dicter ce comportement.

font-display Comportement Quand l'utiliser
auto Comportement par défaut du navigateur (bloque généralement le rendu jusqu'à 3 secondes). Jamais, si vous vous souciez des performances.
swap Affiche immédiatement une police de fallback (FOUT), puis swap vers la police custom quand elle est prête. Corps de texte, articles, éléments d'UI. Les utilisateurs peuvent commencer à lire tout de suite.
block Cache le texte (FOIT) pendant une courte période, puis affiche la police de fallback, puis fait le swap. Icônes ou logos pour lesquels une police de fallback ressemble à du charabia.
fallback Courte période de blocage (block), courte période d'échange (swap). Si ça prend trop de temps, le navigateur abandonne complètement la police custom pour ce chargement de page. Texte décoratif sur des connexions plus lentes.
optional Période de blocage extrêmement courte. Si la police n'est pas en cache ou incroyablement rapide à charger, le navigateur abandonne purement et simplement. Polices "bonus" (nice to have) où la stabilité du layout est primordiale.

Subsetting avec unicode-range

Un fichier de police complet contenant des caractères latins, cyrilliques, grecs et des glyphes CJK (chinois, japonais, coréen) complexes peut peser des mégaoctets.

La propriété unicode-range est un outil d'optimisation brillant. Elle dit au navigateur : "Ce fichier de police couvre uniquement ces caractères spécifiques." Si vous définissez unicode-range: U+0000-00FF (Latin de base), le navigateur va parser votre HTML. S'il ne trouve aucun caractère latin, il ne téléchargera même pas le fichier de police. Cela vous permet de découper d'énormes polices en petits sous-ensembles par langue, en ne chargeant que ce dont l'utilisateur a réellement besoin.

Variable Fonts (polices variables)

Traditionnellement, si vous vouliez du Regular, Italic, Bold, et Bold Italic, vous deviez charger quatre fichiers .woff2 distincts.

Les Variable fonts regroupent toute une famille dans un seul fichier en définissant des "axes de variation". Au lieu de graisses (weights) statiques comme 400 ou 700, une police variable peut être rendue avec une graisse de 432 ou 781.

@font-face {
  font-family: 'MyVariableFont';
  src: url('/fonts/myfont-var.woff2') format('woff2');
  font-weight: 100 900; /* Accepts any weight between 100 and 900 */
}

Intégration en Base64 vs Preloading

Parfois, c'est la requête réseau elle-même qui crée le goulot d'étranglement. Vous disposez de deux stratégies avancées :

  1. Preloading : Ajouter <link rel="preload" href="/fonts/myfont.woff2" as="font" type="font/woff2" crossorigin> dans le <head> de votre HTML dit au navigateur d'aller chercher la police immédiatement, avant même de télécharger le CSS.
  2. Base64 Embedding : Vous pouvez convertir le fichier binaire de la police en une immense chaîne de texte et la coller directement dans l'url() de votre règle @font-face. Ça élimine complètement la requête réseau, mais ça augmente la taille du fichier CSS d'environ 33 %. Cela bloque le rendu de toute la page tant que le CSS n'est pas téléchargé, c'est donc strictement réservé aux toutes petites polices ultra-optimisées (comme un logo custom).

Histoires vécues

Le bouton e-commerce invisible

Un site e-commerce de niche a décidé d'utiliser une police d'icônes personnalisée pour son interface. Ils ont utilisé une déclaration @font-face standard sans font-display. Sur la connexion Wi-Fi rapide du bureau, tout était magnifique.

Cependant, les utilisateurs mobiles en 3G désertaient le site à un rythme record. Quand les développeurs ont simulé un réseau lent, ils ont trouvé le coupable : le navigateur attendait que la police d'icônes soit chargée avant de rendre le bouton "Ajouter au panier". Le résultat ? Un Flash of Invisible Text (FOIT) de 4 secondes. Les utilisateurs ne pouvaient littéralement pas voir le bouton pour donner leur argent à l'entreprise. L'ajout d'un simple font-display: swap; pour le texte et le preloading de la police d'icônes ont sauvé leur taux de conversion.

La tragédie typographique de plusieurs mégaoctets

Un blog tech populaire a décidé de supporter l'internationalisation. Pour s'assurer que leur belle typographie reste cohérente, ils ont inclus une énorme police Noto Sans couvrant tous les caractères CJK (chinois, japonais, coréen). Le fichier de police faisait près de 6 Mo.

D'un coup, les utilisateurs américains lisant des articles en anglais se sont mis à se plaindre de la lenteur du chargement. Leurs navigateurs téléchargeaient 6 Mo de glyphes japonais et coréens juste pour lire un article sur JavaScript. L'équipe a implémenté unicode-range. Ils ont découpé la police en un subset latin de 30 Ko et un subset CJK de 5,9 Mo. Le navigateur s'est mis à requêter automatiquement uniquement le fichier de 30 Ko pour les lecteurs anglophones, guérissant instantanément la chute de performances.

La refonte qui saccade

Une agence de design a conçu une magnifique mise en page éditoriale utilisant neuf graisses différentes d'une police sans-serif classique. Le développeur, zélé, a exporté neuf fichiers .woff2 différents et écrit neuf règles @font-face.

Comme les navigateurs téléchargent les fichiers de manière asynchrone, la page s'affichait par à-coups. La graisse 300 chargeait, puis la 700, puis la 400, provoquant des sauts de layout agressifs (layout shifts) et un recalcul des retours à la ligne quatre ou cinq fois par seconde. Le développeur a remplacé les neuf fichiers par une seule Variable Font (.woff2-variations). Ce fichier unique était 40 % plus léger que les neuf combinés, se chargeait en une seule requête, et affichait la page de manière fluide, en un seul paint.

Pièges et erreurs classiques

  • Servir des formats de police préhistoriques aux navigateurs modernes : Arrêtez de servir des polices .ttf, .eot et .svg à moins d'avoir une raison explicite, basée sur vos analytics, de supporter IE8 ou un appareil Android vieux de dix ans. Restez sur du WOFF2.
  • Oublier crossorigin sur les preloads : Si vous utilisez <link rel="preload"> pour une police, vous devez inclure l'attribut crossorigin, même si la police est hébergée sur votre propre domaine. À cause des spécifications de sécurité des navigateurs, les polices sont récupérées en utilisant un mode anonyme CORS. Si vous omettez cet attribut, le navigateur téléchargera la police deux fois.
  • Encoder de grosses polices de texte en Base64 : Embarquer une police de 100 Ko directement dans votre CSS fait gonfler votre payload CSS et bloque le rendu. N'encodez que des polices minuscules (comme un subset de 5 Ko utilisé pour un logo).
  • Ignorer font-display : Se fier aux comportements par défaut du navigateur punit presque toujours les utilisateurs sur des connexions lentes. Déclarez explicitement font-display: swap pour votre corps de texte afin de prioriser la lecture sur l'esthétique.
  • Preloader toutes les polices de la page : Le preload est une instruction hautement prioritaire. Si vous préchargez une police de titre en gras et italique qui se trouve tout en bas de la page, vous volez de la bande passante au JavaScript et au CSS critiques, nécessaires au chargement du haut de la page. Ne préchargez que la police "hero" (la plus importante au-dessus de la ligne de flottaison).
  • Trop se reposer sur local() : Utiliser local('Helvetica') dit au navigateur d'utiliser une copie locale si elle existe. Cependant, les conventions de nommage des polices diffèrent énormément entre Mac, Windows et Linux. Ce que vous croyez être local('Helvetica Neue Bold') pourrait échouer silencieusement sur un OS différent.

Pourquoi l'avoir sur votre radar

Le développement web est un éternel tir à la corde entre esthétique et performance. Vous construisez une marque, et la typographie est la voix de cette marque. Mais vous construisez aussi un logiciel, et le moindre kilo-octet compte pour un utilisateur connecté sur une ligne de métro capricieuse.

Écrire la règle @font-face parfaite est notoirement fastidieux. Ça demande une compréhension approfondie des caprices des navigateurs, des types MIME, de la syntaxe de subsetting et des formats de compression. Un générateur moderne ne se contente pas d'écrire la syntaxe CSS pour vous ; il encode les bonnes pratiques. Il génère automatiquement les fichiers WOFF2, structure les axes de variables, applique les bons fallbacks font-display et sort exactement les tags de preloading ou de Base64 requis pour contourner les goulots d'étranglement du réseau. Il garantit que, lorsque votre application parle, le navigateur sait réellement comment prononcer les mots.

Pour aller plus loin

Théorie bouclée. Place à la pratique — 100 % dans ton navigateur.

Essayer l'outil: Générateur @font-face