Herramientas de framework para resguardos de fuentes

Janicklas Ralph James
Janicklas Ralph James

Los sitios que cargan fuentes con font-display: swap suelen sufrir un cambio de diseño (CLS) cuando la fuente web se carga y se intercambia con la fuente de resguardo.

Para evitar el CLS, ajusta las dimensiones de la fuente de resguardo para que coincidan con las de la fuente principal. Las propiedades como size-adjust, ascent-override, descent-override y line-gap-override en la regla @font-face pueden ayudar a anular las métricas de una fuente de resguardo, lo que permite a los desarrolladores tener un mayor control sobre cómo se muestran las fuentes. Puedes obtener más información sobre las fuentes y las propiedades de anulación en esta post. También puedes ver una implementación funcional de esta técnica en esta demostración.

En este artículo, se explica cómo se implementan los ajustes de tamaño de fuente en los frameworks Next.js y Nuxt.js para generar las CSS de fuentes de resguardo y reducir el CLS. También se demuestra la manera en que puedes generar fuentes de resguardo con herramientas transversales, como Fontaine y Capsize.

Información general

Por lo general, font-display: swap se usa para evitar FOIT (destello de texto invisible) y mostrar contenido más rápido en la pantalla. El valor de swap indica al navegador que el texto que usa la fuente se debe mostrar de inmediato con una fuente del sistema y que debe reemplazar la fuente del sistema solo cuando la fuente personalizada esté lista.

El mayor problema de swap es el efecto molesto, en el que la diferencia en el tamaño de los caracteres de las dos fuentes hace que el contenido de la pantalla cambie. Esto genera puntuaciones bajas de CLS, especialmente para los sitios web con mucho texto.

En las siguientes imágenes, se muestra un ejemplo del problema. La primera imagen usa font-display: swap sin intentar ajustar el tamaño de la fuente de resguardo. En el segundo ejemplo, se muestra cómo el ajuste del tamaño con la regla @font-face de CSS mejora la experiencia de carga.

Sin ajustar el tamaño de la fuente

body {
  font-family: Inter, serif;
}
Texto que cambia repentinamente de fuente y tamaño, lo que causa un efecto discordante.

Después de ajustar el tamaño de la fuente

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");
}
Texto que cambia de forma fluida a una fuente diferente.

Ajustar el tamaño de la fuente de resguardo puede ser una estrategia eficaz para evitar el cambio del diseño de carga de la fuente, pero implementar la lógica desde cero puede ser complicado, como se describe en esta publicación sobre resguardos de fuentes. Afortunadamente, ya hay varias opciones de herramientas disponibles para facilitar este proceso mientras se desarrollan apps.

Cómo optimizar los resguardos de fuentes con Next.js

Next.js proporciona una forma integrada de habilitar la optimización de fuentes de resguardo. Esta función está habilitada de forma predeterminada cuando cargas fuentes con el componente @next/font.

El componente @next/font se introdujo en la versión 13 de Next.js. El componente proporciona una API para importar fuentes personalizadas o de Google Fonts a tus páginas e incluye alojamiento automático integrado de los archivos de fuentes.

Cuando se usan, las métricas de fuentes de resguardo se calculan automáticamente y se inyectan en el archivo CSS.

Por ejemplo, si usas una fuente Roboto, por lo general, debes definirla en CSS de la siguiente manera:

@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;
}

Para migrar a la siguiente fuente/fuente:

  1. Mueve la declaración de la fuente Roboto a tu JavaScript importando la función "Roboto" desde "next/font". El valor que se muestra de la función será un nombre de clase que puedes aprovechar en tu plantilla de componentes. Recuerda agregar display: swap al objeto de configuración para habilitar la función.

     import { Roboto } from '@next/font/google';
    
    const roboto = Roboto({
      weight: '400',
      subsets: ['latin'],
      display: 'swap' // Using display swap automatically enables the feature
    })
    
  2. En tu componente, usa el nombre de clase generado: javascript export default function RootLayout({ children }: { children: React.ReactNode; }) { return ( <html lang="en" className={roboto.className}> <body>{children}</body> </html> ); }

La opción de configuración adjustFontFallback:

Para @next/font/google: Es un valor booleano que establece si se debe usar una fuente de resguardo automática para reducir el Cambio de diseño acumulado. El valor predeterminado es verdadero. Next.js establece automáticamente la fuente de resguardo en Arial o Times New Roman, según el tipo de fuente (serif frente a sans-serif, respectivamente).

Para @next/font/local: Es una string o un valor booleano falso que establece si se debe usar una fuente de resguardo automática para reducir el Cambio de diseño acumulado. Los valores posibles son Arial, Times New Roman o false. El valor predeterminado es Arial. Si deseas usar una fuente serif, considera establecer este valor en Times New Roman.

Otra opción para las fuentes de Google

Si no es posible usar el componente next/font, otro enfoque para usar esta función con Google Fonts es mediante la marca optimizeFonts. Next.js tiene habilitada la función optimizeFonts de forma predeterminada. Esta función intercala el código CSS de Google Font en la respuesta HTML. Además, puedes habilitar la función de ajuste de resguardo de fuentes si configuras la marca experimental.adjustFontFallbacksWithSizeAdjust en next.config.js, como se muestra en el siguiente fragmento:

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

Nota: No hay plan para admitir esta función con el nuevo dir app. A largo plazo, lo ideal es usar next/font.

Cómo ajustar los resguardos de fuentes con Nuxt

@nuxtjs/fontaine es un módulo para el framework de Nuxt.js que calcula automáticamente los valores de la métrica de la fuente de resguardo y genera el CSS de resguardo @font-face.

Para habilitar el módulo, agrega @nuxtjs/fontaine a la configuración de los módulos:

import { defineNuxtConfig } from 'nuxt'

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

Si usas Google Fonts o no tienes una declaración @font-face para una fuente, puedes declararla como opciones adicionales.

En la mayoría de los casos, el módulo puede leer las reglas de @font-face de tu CSS e inferir automáticamente los detalles, como la familia de fuentes, la familia de fuentes de resguardo y el tipo de visualización.

Si la fuente se define en un lugar que el módulo no puede detectar, puedes pasar la información de las métricas como se muestra en el siguiente fragmento de código.

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

El módulo escanea automáticamente tu CSS para leer las declaraciones @font-face y genera las reglas @font-face de resguardo.

@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%;
}

Ahora puedes usar Roboto override como la fuente de resguardo en tu CSS, como se muestra en el siguiente ejemplo.

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

Genera el CSS tú mismo

Las bibliotecas independientes también pueden ayudarte a generar el CSS para los ajustes de tamaño de fuente de resguardo.

Cómo usar la biblioteca Fontaine

Si no usas Nuxt o Next.js, puedes usar Fontaine. Fontaine es la biblioteca subyacente que impulsa @nuxtjs/fontaine. Puedes usar esta biblioteca en tu proyecto para insertar automáticamente CSS de fuente de resguardo con los complementos Vite o Webpack.

Imagina que tienes una fuente Roboto definida en el archivo 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 proporciona transformadores Vite y Webpack para conectarse fácilmente a la cadena de compilación. Habilítalo como se muestra en el siguiente código de JavaScript.

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 usas Vite, agrega el complemento de la siguiente manera: javascript // Vite export default { plugins: [FontaineTransform.vite(options)] }

Si usas Webpack, habilítalo de la siguiente manera:

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

El módulo analizará automáticamente tus archivos para modificar las reglas @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%; }

Ahora puedes usar Roboto override como fuente de resguardo en CSS. css :root { font-family: 'Roboto'; /* This becomes */ font-family: 'Roboto', 'Roboto override'; }

Usa la biblioteca de Capsize

Si no usas Next.js, Nuxt, Webpack o Vite, otra opción es usar la biblioteca de Capsize para generar el CSS de resguardo.

Nueva API de createFontStack

La API forma parte del paquete @capsize/core llamado createFontStack, que acepta un array de métricas de fuentes en el mismo orden en el que especificarías tu pila de fuentes (la propiedad font-family).

Puede consultar la documentación sobre el uso de Límites aquí.

Ejemplo

Considera el siguiente ejemplo: la fuente web deseada es Lobster, y recurre a Helvetica Neue y luego a Arial. En CSS, font-family: Lobster, 'Helvetica Neue', Arial.

  1. Importa createFontStack desde el paquete principal:

    import { createFontStack } from '@capsizecss/core';
    
  2. Importa las métricas de fuente para cada una de las fuentes deseadas (consulta las métricas de fuente más arriba): javascript import lobster from '@capsizecss/metrics/lobster'; import helveticaNeue from '@capsizecss/metrics/helveticaNeue'; import arial from '@capsizecss/metrics/arial';`

  3. Crea tu pila de fuentes y pasa las métricas como un array con el mismo orden que lo harías con la propiedad de CSS font-family. javascript const { fontFamily, fontFaces } = createFontStack([ lobster, helveticaNeue, arial, ]);

Esto muestra lo siguiente:

{
  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%;
     }
   }
 ]
}

Debes agregar los códigos fontFamily y fontFaces a tu CSS. En el siguiente código, se muestra cómo implementarlo en una hoja de estilo CSS o dentro de un bloque <style>.

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

  
</style>

Esto producirá el siguiente CSS:

.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%;
}

También puedes utilizar el paquete @capsize/metrics para calcular los valores de anulación y aplicarlos al CSS por tu cuenta.

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));

Agradecimientos

Hero image de Alexander Andrews en Unsplash.