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) lorsque la police Web se charge et est remplacée par la police de remplacement.

Vous pouvez empêcher l'affichage du CLS en ajustant les dimensions de la police de remplacement pour qu'elles correspondent à celles de la police principale. Des 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 la façon dont les polices s'affichent. 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 de cette technique dans cette démonstration.

Cet article explique 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 des polices de remplacement et réduire le CLS. Il montre également comment générer des polices de remplacement à l'aide d'outils de coupe transversale tels que Fontaine et Capsize.

Contexte

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

Le principal problème avec swap est l'effet désagréable, où la différence de taille de caractères entre les deux polices entraîne le décalage du contenu à l'écran. Cela entraîne de faibles scores CLS, en particulier pour les sites Web comportant beaucoup de texte.

Les images suivantes illustrent un exemple de ce problème. La première image utilise font-display: swap sans tenter d'ajuster la taille de la police de remplacement. La seconde montre en quoi 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 changent soudainement, avec un effet bruyant.

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 en douceur à une police différente.

Ajuster la taille de la police de remplacement peut être une stratégie efficace pour éviter le décalage de la mise en page lors du chargement de la police. Toutefois, il peut être difficile d'implémenter la logique à partir de zéro, comme décrit dans cet article sur les remplacements de police. Heureusement, plusieurs outils sont déjà disponibles pour faciliter le développement d'applications.

Optimiser les remplacements de polices avec Next.js

Next.js fournit une méthode intégrée pour 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 Next.js version 13. Ce composant fournit une API permettant d'importer des polices Google Fonts ou des polices personnalisées dans vos pages, ainsi qu'une fonctionnalité intégrée d'auto-hébergement automatique des fichiers de police.

Lorsqu'elles sont utilisées, les métriques sur les polices 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:

  1. Déplacez la déclaration de la police Roboto dans votre code JavaScript en important la fonction "Roboto" depuis "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éfinit 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 (respectivement empattement et sans empattement).

Pour @next/font/local:chaîne ou valeur booléenne "false" 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 à empattement, définissez cette valeur sur Times New Roman.

Les polices Google Fonts peuvent aussi

S'il n'est pas possible d'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 dans Next.js. Cette fonctionnalité intègre le code CSS Google Font dans la réponse HTML. En outre, vous pouvez 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: Nous ne prévoyons pas de prendre en charge cette fonctionnalité avec le nouveau répertoire app. À long terme, il est préférable 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 à sa configuration:

import { defineNuxtConfig } from 'nuxt'

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

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

Dans la plupart des cas, le module peut lire les règles @font-face à partir de votre CSS et déduire automatiquement des informations telles 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 des 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 de remplacement @font-face.

@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 permettant d'ajuster la taille de police des créations de remplacement.

Utiliser la bibliothèque Fontaine

Si vous n'utilisez pas Nuxt ni 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 de plug-ins Vite ou Webpack.

Imaginons qu'une police Roboto soit définie 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 pour se connecter facilement à la chaîne de compilation et activer le plug-in, comme indiqué dans le 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 ni Vite, vous pouvez aussi générer le fichier CSS de remplacement à l'aide de la bibliothèque Capsize.

Ajout de l'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 (la propriété font-family).

Vous pouvez consulter la documentation sur l'utilisation de Capsize ici.

Exemple

Prenons l'exemple suivant: la police Web souhaitée est "Lobster", puis Helvetica Neue et Arial. En CSS, font-family: Lobster, 'Helvetica Neue', Arial.

  1. Importez createFontStack à partir du package principal:

    import { createFontStack } from '@capsizecss/core';
    
  2. Importez les métriques de police pour chacune des polices souhaitées (voir "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 la forme d'un tableau, en suivant 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 produit:

.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 principale créée par Alexander Andrews sur Unsplash.