Outils de framework pour les polices de remplacement

Janicklas Ralph James
Janicklas Ralph James

Les sites qui chargent des polices avec font-display: swap souffrent souvent d'un décalage de mise en page (CLS) lors du chargement de la police Web et de son remplacement par la police de remplacement.

Vous pouvez empêcher le CLS en ajustant les dimensions de la police de remplacement afin qu'elles correspondent à celles de la police principale. Les propriétés telles que size-adjust, ascent-override, descent-override et line-gap-override dans la règle @font-face peuvent aider à remplacer les métriques d'une police de remplacement, ce qui permet aux développeurs de mieux contrôler l'affichage des polices. Pour en savoir plus sur les polices de remplacement et les propriétés de remplacement, consultez cet article. Vous pouvez également voir une implémentation fonctionnelle de cette technique dans cette démonstration.

Cet article décrit comment les ajustements de taille de police sont implémentés dans les frameworks Next.js et Nuxt.js pour générer le CSS de la police de remplacement et réduire le CLS. Il montre également comment générer des polices de remplacement à l'aide d'outils transversaux tels que Fontaine et Capsize.

Contexte

font-display: swap est généralement utilisé pour empêcher le FOIT (Flash de texte invisible) et pour afficher le contenu plus rapidement à l'écran. La valeur de swap indique au navigateur que le texte utilisant cette police doit s'afficher immédiatement avec une police système et qu'il ne doit remplacer la police système que lorsque la police personnalisée est prête.

Le plus gros problème de swap est l'effet de bouleversement, où la différence de taille des caractères entre les deux polices entraîne le déplacement du contenu à l'écran. Cela conduit à de faibles scores CLS, en particulier pour les sites Web contenant beaucoup de texte.

Les images suivantes illustrent un exemple de ce problème. La première image utilise font-display: swap sans aucune tentative d'ajuster la taille de la police de remplacement. La seconde montre comment l'ajustement de la taille à l'aide de la règle CSS @font-face améliore l'expérience de chargement.

Sans ajuster la taille de la police

body {
  font-family: Inter, serif;
}
Texte dont la police et la taille des caractères sont modifiées soudainement, ce qui génère un effet gênant.

Après avoir ajusté la taille de la police

body {
  font-family: Inter, fallback-inter, serif;
  }

@font-face {
  font-family: "fallback-inter";
  ascent-override: 90.20%;
  descent-override: 22.48%;
  line-gap-override: 0.00%;
  size-adjust: 107.40%;
  src: local("Arial");
}
Texte qui passe de façon fluide à une autre police d'écriture

L'ajustement de la taille de la police de remplacement peut s'avérer efficace pour empêcher le chargement des polices avec un décalage de mise en page. Toutefois, il peut être difficile d'implémenter la logique de A à Z, comme décrit dans cet article sur les polices de remplacement. Heureusement, plusieurs outils sont déjà disponibles pour faciliter le développement d'applications.

Optimiser les polices de remplacement avec Next.js

Next.js offre un moyen intégré d'activer l'optimisation des polices de remplacement. Cette fonctionnalité est activée par défaut lorsque vous chargez des polices à l'aide du composant @next/font.

Le composant @next/font a été introduit dans la version 13 de Next.js. Le composant fournit une API permettant d'importer des polices Google Fonts ou des polices personnalisées dans vos pages, et intègre l'auto-hébergement automatique des fichiers de police.

Lorsqu'elles sont utilisées, les métriques de police de remplacement sont automatiquement calculées et injectées dans le fichier CSS.

Par exemple, si vous utilisez une police Roboto, vous devez généralement la définir en CSS comme suit:

@font-face {
  font-family: 'Roboto';
  font-display: swap;
  src: url('/fonts/Roboto.woff2') format('woff2'), url('/fonts/Roboto.woff') format('woff');
  font-weight: 700;
}

body {
  font-family: Roboto;
}

Pour migrer vers la police suivante ou la suivante:

  1. Déplacez la déclaration de police Roboto dans votre code JavaScript en important la fonction "Roboto" de "next/font". La valeur renvoyée par la fonction sera un nom de classe que vous pourrez exploiter dans votre modèle de composant. N'oubliez pas d'ajouter display: swap à l'objet de configuration pour activer la fonctionnalité.

     import { Roboto } from '@next/font/google';
    
    const roboto = Roboto({
      weight: '400',
      subsets: ['latin'],
      display: 'swap' // Using display swap automatically enables the feature
    })
    
  2. Dans votre composant, utilisez le nom de classe généré : javascript export default function RootLayout({ children }: { children: React.ReactNode; }) { return ( <html lang="en" className={roboto.className}> <body>{children}</body> </html> ); }

L'option de configuration adjustFontFallback:

Pour @next/font/google:valeur booléenne qui détermine si une police de remplacement automatique doit être utilisée pour réduire le décalage de mise en page cumulatif. La valeur par défaut est "true". Next.js définit automatiquement votre police de remplacement sur Arial ou Times New Roman en fonction du type de police (Serif et Sans Serif, respectivement).

Pour @next/font/local:chaîne ou valeur booléenne fausse qui détermine si une police de remplacement automatique doit être utilisée pour réduire le décalage de mise en page cumulatif. Les valeurs possibles sont Arial, Times New Roman ou false. La valeur par défaut est Arial. Si vous souhaitez utiliser une police Serif, définissez cette valeur sur Times New Roman.

Autre option pour Google Fonts

Si vous ne pouvez pas utiliser le composant next/font, vous pouvez aussi utiliser l'indicateur optimizeFonts pour utiliser cette fonctionnalité avec Google Fonts. La fonctionnalité OptimizeFonts est déjà activée par défaut pour Next.js. Cette fonctionnalité intègre le CSS Google Fonts dans la réponse HTML. Vous pouvez également activer la fonctionnalité d'ajustement des polices de remplacement en définissant l'indicateur experimental.adjustFontFallbacksWithSizeAdjust dans votre fichier next.config.js, comme indiqué dans l'extrait de code suivant:

// In next.config.js
module.exports = {
 experimental: {
   adjustFontFallbacksWithSizeAdjust: true,
 },
}

Remarque: Il n'est pas prévu que cette fonctionnalité soit compatible avec le nouveau répertoire app. À long terme, il est recommandé d'utiliser next/font.

Ajuster les polices de remplacement avec Nuxt

@nuxtjs/fontaine est un module pour le framework Nuxt.js qui calcule automatiquement les valeurs des métriques de police de remplacement et génère le CSS @font-face de remplacement.

Activez le module en ajoutant @nuxtjs/fontaine à la configuration de vos modules:

import { defineNuxtConfig } from 'nuxt'

export default defineNuxtConfig({
  modules: ['@nuxtjs/fontaine'],
})

Si vous utilisez Google Fonts ou si vous n'avez pas de déclaration @font-face pour une police, vous pouvez la déclarer comme des options supplémentaires.

Dans la plupart des cas, le module peut lire les règles @font-face de votre CSS et déduire automatiquement des détails tels que la famille de polices, la famille de polices de remplacement et le type d'affichage.

Si la police est définie à un emplacement non visible par le module, vous pouvez transmettre les informations sur les métriques, comme indiqué dans l'extrait de code suivant.

export default defineNuxtConfig({
  modules: ['@nuxtjs/fontaine'],
  fontMetrics: {
  fonts: ['Inter', { family: 'Some Custom Font', src: '/path/to/custom/font.woff2' }],
},
})

Le module analyse automatiquement votre CSS pour lire les déclarations @font-face et génère les règles @font-face de remplacement.

@font-face {
  font-family: 'Roboto';
  font-display: swap;
  src: url('/fonts/Roboto.woff2') format('woff2'), url('/fonts/Roboto.woff') format('woff');
  font-weight: 700;
}
/* This will be generated. */
@font-face {
  font-family: 'Roboto override';
  src: local('BlinkMacSystemFont'), local('Segoe UI'), local('Roboto'), local('Helvetica Neue'),
    local('Arial'), local('Noto Sans');
  ascent-override: 92.7734375%;
  descent-override: 24.4140625%;
  line-gap-override: 0%;
}

Vous pouvez maintenant utiliser Roboto override comme police de remplacement dans votre CSS, comme illustré dans l'exemple suivant.

:root {
  font-family: 'Roboto';
  /* This becomes */
  font-family: 'Roboto', 'Roboto override';
}

Générer le CSS vous-même

Les bibliothèques autonomes peuvent également vous aider à générer le code CSS pour les ajustements de taille de police de remplacement.

Utiliser la bibliothèque Fontaine

Si vous n'utilisez pas Nuxt ou Next.js, vous pouvez utiliser Fontaine. Fontaine est la bibliothèque sous-jacente qui alimente @nuxtjs/fontaine. Vous pouvez utiliser cette bibliothèque dans votre projet pour injecter automatiquement du code CSS de remplacement à l'aide des plug-ins Vite ou Webpack.

Imaginons que vous ayez défini une police Roboto dans le fichier CSS:

@font-face {
  font-family: 'Roboto';
  font-display: swap;
  src: url('/fonts/Roboto.woff2') format('woff2'), url('/fonts/Roboto.woff') format('woff');
  font-weight: 700;
}

Fontaine fournit des transformateurs Vite et Webpack qui permettent de se connecter facilement à la chaîne de compilation. Activez le plug-in comme indiqué dans le code JavaScript suivant.

import { FontaineTransform } from 'fontaine'

const options = {
  fallbacks: ['BlinkMacSystemFont', 'Segoe UI', 'Helvetica Neue', 'Arial', 'Noto Sans'],
  // You may need to resolve assets like `/fonts/Roboto.woff2` to a particular directory
  resolvePath: (id) => 'file:///path/to/public/dir' + id,
  // overrideName: (originalName) => `${name} override`
  // sourcemap: false
}

Si vous utilisez Vite, ajoutez le plug-in comme suit : javascript // Vite export default { plugins: [FontaineTransform.vite(options)] }

Si vous utilisez Webpack, activez-le comme suit:

// Webpack
export default {
  plugins: [FontaineTransform.webpack(options)]
}

Le module analyse automatiquement vos fichiers pour modifier les règles @font-face : css @font-face { font-family: 'Roboto'; font-display: swap; src: url('/fonts/Roboto.woff2') format('woff2'), url('/fonts/Roboto.woff') format('woff'); font-weight: 700; } /* This will be generated. */ @font-face { font-family: 'Roboto override'; src: local('BlinkMacSystemFont'), local('Segoe UI'), local('Roboto'), local('Helvetica Neue'), local('Arial'), local('Noto Sans'); ascent-override: 92.7734375%; descent-override: 24.4140625%; line-gap-override: 0%; }

Vous pouvez désormais utiliser Roboto override comme police de remplacement en CSS. css :root { font-family: 'Roboto'; /* This becomes */ font-family: 'Roboto', 'Roboto override'; }

Utiliser la bibliothèque Capsize

Si vous n'utilisez pas Next.js, Nuxt, Webpack ou Vite, vous pouvez également utiliser la bibliothèque Capsize pour générer le code CSS de remplacement.

Nouvelle API createFontStack

L'API fait partie du package @capsize/core appelé createFontStack, qui accepte un tableau de métriques de police dans le même ordre que celui que vous spécifiez pour votre pile de polices (propriété font-family).

Pour consulter la documentation sur l'utilisation de Capsize, cliquez ici.

Exemple

Prenons l'exemple suivant: la police Web souhaitée est Lobster, en revenant à Helvetica Neue, puis Arial. Dans CSS, font-family: Lobster, 'Helvetica Neue', Arial.

  1. Importez createFontStack à partir du package de base:

    import { createFontStack } from '@capsizecss/core';
    
  2. Importez les métriques de police pour chacune des polices souhaitées (voir "Font Metrics" (Métriques de police) ci-dessus) : javascript import lobster from '@capsizecss/metrics/lobster'; import helveticaNeue from '@capsizecss/metrics/helveticaNeue'; import arial from '@capsizecss/metrics/arial';`

  3. Créez votre pile de polices en transmettant les métriques sous forme de tableau, dans le même ordre que pour la propriété CSS "font-family". javascript const { fontFamily, fontFaces } = createFontStack([ lobster, helveticaNeue, arial, ]);

Cela renvoie le résultat suivant :

{
  fontFamily: Lobster, 'Lobster Fallback: Helvetica Neue', 'Lobster Fallback: Arial',
  fontFaces: [
    {
      '@font-face' {
      'font-family': '"Lobster Fallback: Helvetica Neue"';
      src: local('Helvetica Neue');
      'ascent-override': '115.1741%';
      'descent-override': '28.7935%';
      'size-adjust': '86.8251%';
      }
     '@font-face' {
       'font-family': '"Lobster Fallback: Arial"';
       src: local('Arial');
       'ascent-override': 113.5679%;
       'descent-override': 28.392%;
       'size-adjust': 88.053%;
     }
   }
 ]
}

Vous devez ajouter le code fontFamily et fontFaces à votre CSS. Le code suivant montre comment l'implémenter dans une feuille de style CSS ou dans un bloc <style>.

<style type="text/css">
  .heading {
    font-family: 
  }

  
</style>

Le code CSS suivant est alors généré:

.heading {
  font-family: Lobster, 'Lobster Fallback: Helvetica Neue',
    'Lobster Fallback: Arial';
}

@font-face {
  font-family: 'Lobster Fallback: Helvetica Neue';
  src: local('Helvetica Neue');
  ascent-override: 115.1741%;
  descent-override: 28.7935%;
  size-adjust: 86.8251%;
}
@font-face {
  font-family: 'Lobster Fallback: Arial';
  src: local('Arial');
  ascent-override: 113.5679%;
  descent-override: 28.392%;
  size-adjust: 88.053%;
}

Vous pouvez également utiliser le package @capsize/metrics pour calculer les valeurs de remplacement et les appliquer vous-même au CSS.

const fontMetrics = require(`@capsizecss/metrics/inter`);
const fallbackFontMetrics = require(`@capsizecss/metrics/arial`);
const mainFontAvgWidth = fontMetrics.xAvgWidth / fontMetrics.unitsPerEm;
const fallbackFontAvgWidth = fallbackFontMetrics.xAvgWidth / fallbackFontMetrics.unitsPerEm;
let sizeAdjust = mainFontAvgWidth / fallbackFontAvgWidth;
let ascent = fontMetrics.ascent / (unitsPerEm * fontMetrics.sizeAdjust));
let descent = fontMetrics.descent / (unitsPerEm * fontMetrics.sizeAdjust));
let lineGap = fontMetrics.lineGap / (unitsPerEm * fontMetrics.sizeAdjust));

Remerciements

Image héros par Alexander Andrews sur Unsplash.