Saltar al contenido
Construcción, optimización y experimentos web[email protected]
La Trastienda WebLo que pasa detrás de una web
Optimización

Tipografías web sin frenar la carga

font-display, precarga, subconjuntos para español y métricas de la fuente de reserva, con un cálculo de peso entre cuatro archivos estáticos y uno variable.

Por Redacción de La Trastienda WebPublicado el 7 min de lectura
Caja de tipos móviles de imprenta con letras sueltas, entre ellas la eñe y los signos de apertura de interrogación y exclamación

Una fuente web puede causar dos problemas distintos. Mientras se descarga, el navegador puede esconder el texto, y entonces el contenido tarda más en verse y el LCP sube si ese texto es el elemento más grande. Cuando por fin llega, el texto puede cambiar de anchura y altura y empujar lo que hay debajo, y eso sube el CLS. Casi todo lo que sigue sirve para controlar uno de esos dos momentos.

Primera decisión: ¿necesitas una fuente web?

La opción más rápida es no descargar nada. Una pila de fuentes del sistema usa lo que ya tiene instalado cada dispositivo:

body {
  font-family: system-ui, -apple-system, "Segoe UI", Roboto, sans-serif;
}

Cero bytes, cero cambios de fuente y buena legibilidad en pantalla. El precio es que el texto se verá algo distinto en Windows, macOS, Android o iOS. En textos de lectura larga, documentación o paneles internos, eso rara vez importa. Si la tipografía es parte de la identidad (un titular, un logotipo en texto), una vía intermedia es usar fuente web sólo para títulos y del sistema para el cuerpo.

font-display: qué ve el usuario mientras tanto

El descriptor font-display, dentro de @font-face, define tres periodos. En el de bloqueo, el texto se dibuja invisible; en el de intercambio, se muestra con la fuente de reserva y se sustituye si la web llega; después, si no ha llegado, se queda la de reserva. Los valores recomendados por la especificación, que recogen MDN y Chrome for Developers, son estos (cada navegador puede variarlos):

Valores de font-display
ValorBloqueoIntercambioEfecto práctico
blockCorto (se recomiendan 3 s)InfinitoTexto invisible hasta 3 s; malo para LCP
swapMínimoInfinitoTexto visible al momento; el cambio tardío puede mover el diseño
fallbackMuy corto (≤ 100 ms)Corto (3 s)Si la fuente tarda más de unos 3 s, se queda la de reserva en esa carga
optionalMuy corto (≤ 100 ms)NingunoSólo usa la fuente si está casi lista; sin cambio posterior
autoLo decide el navegadorLo decide el navegadorSuele comportarse como block

Para texto de lectura, la elección real está entre swap y optional. Con swap la fuente acaba apareciendo siempre, pero el cambio puede producir un salto. optional evita el salto: según web.dev, si la fuente no llega en unos 100 ms no se usa en esa página, aunque queda en caché y las siguientes navegaciones ya la tendrán. El coste es que en una primera visita lenta el usuario verá la fuente de reserva. fallback queda en medio y es razonable cuando no te convence ninguno de los dos extremos.

WOFF2 y subconjuntos para español

web.dev indica que WOFF2 es el único formato que necesitan hoy los navegadores modernos, y el más compacto. Ofrecer además WOFF o TTF en el src sólo tiene sentido si das soporte a navegadores muy antiguos.

La otra gran palanca es quitar glifos que no usarás. Una fuente completa puede incluir cirílico, griego, vietnamita y cientos de símbolos. Para un sitio en español bastan, en la práctica:

  • Latín básico (U+0020–007E): letras, números y puntuación ASCII.
  • Suplemento Latín-1 (U+00A0–00FF): aquí están á é í ó ú, sus mayúsculas, ñ Ñ, ü Ü, ¿ (U+00BF), ¡ (U+00A1) y las comillas « » (U+00AB, U+00BB).
  • Unos pocos signos de puntuación general: rayas y guiones (U+2013–2014), comillas tipográficas (U+2018–201D), puntos suspensivos (U+2026) y el euro (U+20AC).

Hay que distinguir dos cosas que a menudo se confunden. El descriptor unicode-range no recorta el archivo: sólo le dice al navegador para qué caracteres sirve ese archivo, y web.dev explica que el navegador lo descarga si la página contiene algún carácter del rango. El recorte real se hace antes, con una herramienta de subconjuntos (por ejemplo pyftsubset, de la biblioteca fontTools) que genera un WOFF2 sólo con esos glifos. Lo habitual es combinar ambas cosas:

@font-face {
  font-family: "Texto";
  src: url("/fonts/texto-latin.woff2") format("woff2");
  font-weight: 400 700;
  font-display: swap;
  unicode-range: U+0000-00FF, U+2013-2014, U+2018-201D, U+2026, U+20AC;
}

Cuatro archivos estáticos o uno variable

Una fuente variable contiene un rango de grosores (eje wght) en un solo archivo. Pesa más que una variante estática suelta, pero menos que varias juntas. Cuál conviene depende de cuántas variantes uses de verdad.

Ejemplo de cálculo

Supongamos una familia, ya recortada al subconjunto latino y en WOFF2, con estos pesos (cifras inventadas pero de un orden de magnitud habitual): cada variante estática pesa 24 KB y la variable con eje de grosor 400–700 pesa 56 KB.

  • Cuatro estáticas (400, 500, 600, 700): 4 × 24 = 96 KB en 4 peticiones.
  • Una variable: 56 KB en 1 petición. Ahorro: 40 KB (−42 %).
  • Si sólo usas 400 y 700: 2 × 24 = 48 KB, menos que la variable. Aquí ganan las estáticas.

El punto de equilibrio en este supuesto está en 56 / 24 ≈ 2,3 variantes: con tres o más, la variable sale a cuenta. La cursiva suele ir en un archivo aparte en ambos casos, así que súmala por separado. Haz la cuenta con los pesos reales de tu familia: la proporción cambia mucho de una fuente a otra.

Hay un detalle que el número de kilobytes no refleja: con estáticas, sólo se descargan las variantes que la página usa de verdad, mientras que la variable se descarga entera aunque uses un solo grosor en esa página.

Precargar sólo la fuente crítica

El navegador no sabe que necesita una fuente hasta que ha descargado el CSS y encontrado un elemento que la usa. Una precarga en el <head> adelanta la petición:

<link rel="preload" href="/fonts/texto-latin.woff2"
      as="font" type="font/woff2" crossorigin>

MDN indica que las precargas de fuentes requieren el atributo crossorigin incluso si el archivo está en tu propio dominio, porque las fuentes se piden en modo CORS anónimo; sin él, la precarga no coincide con la petición real y la fuente se descarga dos veces. web.dev añade dos advertencias: la precarga ignora unicode-range, y precargar demasiado quita ancho de banda a otros recursos. Regla práctica: precarga uno, como mucho dos archivos, los que pintan el texto del primer pantallazo. Con font-display: optional, la precarga es casi obligatoria si quieres que la fuente llegue a tiempo en la primera visita.

Ajustar la fuente de reserva para que el cambio no mueva nada

El salto al cambiar de fuente ocurre porque la de reserva (Arial, por ejemplo) ocupa distinto espacio que la web: letras más anchas hacen que el texto ocupe más líneas, y alturas de línea distintas cambian el alto de cada párrafo. web.dev documenta el descriptor size-adjust, que escala los glifos de una fuente, junto con ascent-override, descent-override y line-gap-override, que fijan las métricas verticales. Se aplican a una fuente de reserva declarada con local():

@font-face {
  font-family: "Texto Reserva";
  src: local("Arial");
  size-adjust: 97%;
  ascent-override: 92%;
  descent-override: 24%;
  line-gap-override: 0%;
}
body {
  font-family: "Texto", "Texto Reserva", sans-serif;
}

Los porcentajes de este fragmento son sólo de muestra: los correctos dependen de las métricas de tu fuente web y de la de reserva elegida, y se calculan con herramientas que leen ambos archivos o ajustándolos a ojo superponiendo las dos versiones. Entre fuentes proporcionales distintas los glifos nunca coinciden letra a letra, pero se puede acercar tanto el ancho total que los cortes de línea casi no cambien. Para ver cómo afecta este salto a la métrica, está el cálculo de ventanas de sesión en LCP, INP y CLS: qué miden.

Dos párrafos comparados: con la fuente de reserva sin ajustar ocupa cuatro líneas y con la web tres, lo que desplaza el bloque inferior; con la reserva ajustada ambos ocupan tres líneas
Si la reserva ocupa las mismas líneas que la fuente web, el intercambio no desplaza el contenido de abajo.

Ruta de decisión

Diagrama de decisión: si la fuente no es imprescindible, usar fuentes del sistema; si lo es, WOFF2 con subconjunto; según si falla el CLS, usar optional con precarga o swap con reserva ajustada
De cero bytes a la fuente web ajustada, en tres preguntas.
  1. ¿La tipografía aporta algo que el usuario note? Si no, usa la pila del sistema y has terminado.
  2. ¿Cuántas variantes usas de verdad? Haz inventario en el CSS. Elimina las que aparecen en una sola regla. Con tres o más grosores, compara con la versión variable.
  3. Prepara los archivos: sólo WOFF2, recortados al subconjunto latino que necesita el español, con su unicode-range. Alojarlos en tu dominio evita una conexión a otro origen.
  4. ¿Tu CLS de campo está por encima de 0,1 y el cambio de fuente contribuye? Usa font-display: optional más precarga del archivo del cuerpo de texto. Si no, swap con una fuente de reserva ajustada con size-adjust y las tres anulaciones de métricas.
  5. Precarga sólo el archivo que pinta el primer pantallazo, con crossorigin.

Comprueba el resultado en DevTools con la red limitada: el texto debe verse enseguida y, si hay cambio de fuente, no debe mover los bloques de abajo. Si a la vez estás revisando las imágenes de la misma plantilla, los atributos que evitan saltos están en imágenes ligeras y sin saltos de diseño. Y si vas a medir el efecto del cambio, plantéalo antes como se describe en cómo montar un experimento web, porque el beneficio de una fuente bien cargada suele ser de decenas de kilobytes y fácil de confundir con el ruido.

Cómo se ha elaborado este artículo

Se basa en las páginas de MDN sobre font-display, rel=preload y unicode-range, y en los artículos de web.dev sobre buenas prácticas de fuentes y size-adjust. Los pesos de archivo del ejemplo son cifras inventadas para mostrar el razonamiento, no mediciones de fuentes concretas. No incluye pruebas propias.

Fuentes consultadas

  1. MDN Web Docs: font-display
  2. MDN Web Docs: FontFace: display property
  3. MDN Web Docs: rel=preload
  4. web.dev: Best practices for fonts
  5. web.dev: Optimize web fonts
  6. web.dev: CSS size-adjust for @font-face
  7. Chrome for Developers: Controlling Font Performance with font-display

¿Has visto un dato erróneo o desactualizado? Escribe a [email protected] indicando la URL. Las correcciones se aplican según la política editorial.