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 est chargée et remplacée par la police de remplacement.
Vous pouvez éviter le CLS en ajustant les dimensions de la police de remplacement pour 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 forçage, consultez cet article. Vous pouvez également voir une implémentation fonctionnelle de cette technique dans cette démonstration.
Cet article explique comment les ajustements de la 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 éviter le FOIT (Flash of invisible text) et pour afficher les contenus plus rapidement à l'écran. La valeur swap
indique au navigateur que le texte utilisant la police doit être affiché immédiatement à l'aide d'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 avec swap
est l'effet discordant, où la différence de taille des caractères des deux polices entraîne un décalage du contenu de l'écran. Cela entraîne de faibles scores CLS, en particulier pour les sites Web contenant beaucoup de texte.
Les images suivantes illustrent ce problème. La première image utilise font-display: swap
sans aucune tentative d'ajustement de la taille de la police de remplacement. Le second montre comment ajuster 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;
}
Après avoir ajusté la taille de 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");
}
Ajuster la taille de la police de remplacement peut être une stratégie efficace pour éviter le décalage de mise en page lors du chargement de la police, mais implémenter la logique à partir de zéro peut s'avérer délicat, comme décrit dans cet article sur les polices de remplacement. Heureusement, plusieurs options d'outils sont déjà disponibles pour faciliter le développement d'applications.
Optimiser les polices de remplacement avec Next.js
Next.js fournit une méthode intégrée pour activer l'optimisation des polices de secours. 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 pour 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 la définissez généralement 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 "next/font" :
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 })
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 indiquant si une police de remplacement automatique doit être utilisée pour réduire le décalage de mise en page cumulé. 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 false qui indique si une police de remplacement automatique doit être utilisée pour réduire le décalage de mise en page cumulé. Les valeurs possibles sont Arial
, Times New Roman
ou false
. La valeur par défaut est Arial
. Si vous souhaitez utiliser une police Serif, envisagez de définir cette valeur sur Times New Roman
.
Autre option pour Google Fonts
Si vous ne pouvez pas utiliser le composant next/font
, vous pouvez 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 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 : Nous ne prévoyons pas de prendre en charge cette fonctionnalité avec le nouveau répertoire app
. À long terme, il est idéal 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 de métrique 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 inférer automatiquement les 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 dans un emplacement non détectable 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 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 dans 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 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 vous spécifieriez votre pile de polices (propriété font-family
).
Vous pouvez consulter la documentation sur l'utilisation de Capsize sur cette page.
Exemple
Prenons l'exemple suivant : la police Web souhaitée est Lobster, puis Helvetica Neue, puis Arial. En CSS, font-family: Lobster, 'Helvetica Neue', Arial
.
Importez createFontStack à partir du package de base :
import { createFontStack } from '@capsizecss/core';
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';`
Créez votre pile de polices, en transmettant les métriques sous forme de tableau, en utilisant le même ordre que vous le feriez via 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>
Vous obtiendrez le code CSS suivant :
.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 forçage 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));