Abres una web y, durante un instante, los titulares no están. O están con una letra que no es la de la marca y, medio segundo después, todo se recoloca. Es el efecto de las fuentes web: el navegador tiene que descargarlas antes de poder pintar el texto con ellas, y cómo gestiones esa espera decide si el texto aparece al instante o si la página se mueve mientras lees.
Vamos de lo que ocurre en el navegador a lo que puedes cambiar hoy en tu CSS y tu HTML: font-display, WOFF2, subconjuntos, precarga, fuente de reserva ajustada y cómo medirlo todo.
Cómo afectan las fuentes al LCP y al CLS
El navegador no descarga una fuente por el hecho de declararla en @font-face: espera a que haya texto en la página que la use. La descarga empieza tarde, después de bajar y procesar el CSS. Mientras llega el archivo, tiene que decidir qué hacer con el texto, y hay dos comportamientos clásicos:
- FOIT (flash of invisible text): el texto se oculta hasta que la fuente está lista. Si tarda, el usuario ve huecos en blanco.
- FOUT (flash of unstyled text): el texto se muestra enseguida con una fuente de reserva y se cambia por la web cuando llega. Se lee desde el principio, pero se nota el cambio.
El LCP mide cuándo se pinta el elemento de contenido más grande de la zona visible. A menudo es una imagen, pero muchas veces es un titular o un párrafo grande. Si ese texto está oculto esperando la fuente, el LCP no puede producirse hasta que llegue. Google considera bueno un LCP de 2,5 segundos o menos, medido en el percentil 75 de las visitas reales.
El CLS mide los desplazamientos inesperados de contenido. Si la fuente de reserva es más ancha o más alta que la definitiva, al cambiarla las líneas se parten de otra manera y los bloques crecen o encogen, empujando lo que hay debajo. Un CLS bueno es 0,1 o menos.
No existe un ajuste gratis: ocultar el texto evita el salto pero retrasa el LCP; mostrarlo antes lo adelanta pero arriesga el CLS. La salida es doble: que la fuente llegue antes (formato, tamaño, precarga) y que el cambio apenas se note (reserva ajustada).
font-display: qué hace cada valor
La propiedad font-display, dentro de @font-face, le dice al navegador cuánto esperar y qué mostrar. Funciona con dos periodos: el de bloqueo (texto invisible) y el de intercambio (texto con la reserva, que se cambia si la fuente llega). Los tiempos exactos los deciden los navegadores; los de la tabla son orientativos.
| Valor | Bloqueo | Intercambio | Cuándo usarlo |
|---|---|---|---|
auto | Lo decide el navegador | Lo decide el navegador | Mejor no dejarlo por defecto |
block | Unos 3 s | Infinito | Fuentes de iconos |
swap | Casi nulo | Infinito | Opción general, con reserva ajustada |
fallback | Unos 100 ms | Unos 3 s | Compromiso si el cambio tardío te preocupa |
optional | Unos 100 ms | Ninguno | Fuentes secundarias o CLS cero |
Mi criterio: swap con una reserva ajustada para casi todo. fallback si te preocupa que el cambio ocurra cuando el usuario ya está leyendo. optional si el CLS cero es prioridad y aceptas que en la primera visita se vea la reserva (con precarga tiene más opciones de llegar a tiempo). block, solo para fuentes de iconos. Y no dejes auto: su comportamiento depende del navegador.
@font-face {
font-family: "Inter";
src: url("/fonts/inter-latin.woff2") format("woff2");
font-weight: 100 900;
font-style: normal;
font-display: swap;
}
WOFF2, fuentes variables y subconjuntos
Hoy basta un formato: WOFF2. Comprime con Brotli, pesa bastante menos que TTF u OTF y lo admiten todos los navegadores actuales (comprueba la tabla de compatibilidad si tienes que dar soporte a algo muy antiguo). EOT era para versiones viejas de Internet Explorer, y TTF, OTF o WOFF1 solo añaden peso y líneas de CSS. Con una única URL en src es suficiente. Como WOFF2 ya viene comprimido, no esperes ahorros extra si además lo sirves con gzip; lo que sí conviene es que el servidor lo entregue con el tipo correcto (font/woff2).
Una fuente estática es un archivo por peso y estilo: regular, negrita, cursiva y negrita cursiva son cuatro descargas. Una fuente variable guarda en un solo archivo un rango continuo (por ejemplo, pesos de 100 a 900). No siempre pesa menos: con muchos ejes puede superar a una estática. Suele compensar cuando usas tres o más pesos de la misma familia; con solo regular y negrita, dos estáticas con subconjunto pueden ganar. Mide ambas opciones.
El subsetting es la mayor palanca. Una fuente completa incluye miles de glifos (cirílico, griego, vietnamita, símbolos) y tu web en español usa unos cientos. Con fonttools puedes generar un archivo solo con lo necesario: latín básico, el suplemento Latin-1 (acentos, ñ, ¿, ¡), puntuación tipográfica y el euro.
pip install fonttools brotli
pyftsubset Inter.ttf --unicodes="U+0020-007E,U+00A0-00FF,U+2013-2014,U+2018-201D,U+2026,U+20AC" --layout-features="*" --flavor=woff2 --output-file=inter-latin.woff2
Revisa la licencia antes de modificar una fuente: algunas exigen cambiar el nombre al distribuir una versión modificada. Y comprueba tu contenido: si escribes en catalán o publicas nombres con caracteres poco comunes, un glifo ausente se pintará con la fuente de reserva.
Con varios idiomas puedes partir la fuente en subconjuntos y declarar cada uno con unicode-range: el navegador descarga solo los que la página necesita. En un sitio solo en español, un único archivo latino suele ser lo más simple.
@font-face {
font-family: "Inter";
src: url("/fonts/inter-latin.woff2") format("woff2");
font-weight: 100 900;
font-display: swap;
unicode-range: U+0020-007E, U+00A0-00FF, U+2013-2014, U+2018-201D, U+2026, U+20AC;
}
@font-face {
font-family: "Inter";
src: url("/fonts/inter-latin-ext.woff2") format("woff2");
font-weight: 100 900;
font-display: swap;
unicode-range: U+0100-017F;
}
Precarga, autoalojamiento y Google Fonts
Como el navegador descubre la fuente tarde, puedes avisarle desde el HTML con una precarga:
<link rel="preload" href="/fonts/inter-latin.woff2" as="font" type="font/woff2" crossorigin>
Tiene varias trampas:
- El atributo
crossorigines obligatorio, incluso si la fuente está en tu mismo dominio. Las fuentes se piden en modo CORS anónimo; sin el atributo, la precarga no se reutiliza y el archivo se descarga dos veces. - Precarga solo las críticas: una o dos, las de la primera pantalla. Cada precarga compite con el CSS y con la imagen del LCP.
- La URL debe coincidir exactamente con la del
@font-face. - No precargues subconjuntos que quizá no se usen: la precarga los descarga siempre. Chrome avisa en la consola si precargas algo que no se utiliza.
Una precarga no arregla un mal montaje. Declara el @font-face en el CSS principal (o en el CSS crítico en línea), evita las cadenas de @import y no dependas de un script que cargue las fuentes tarde: cada eslabón retrasa el momento en que el navegador sabe qué tiene que descargar.
Si usas Google Fonts, la página pide el CSS a un dominio y los archivos a otro (fonts.googleapis.com y fonts.gstatic.com): dos conexiones nuevas, y un CSS que bloquea el renderizado antes de que se descubra la fuente. Autoalojada va por la misma conexión, controlas el subconjunto y puedes precargarla. El viejo argumento de que "ya estará en la caché del visitante" ya no vale: los navegadores principales separan la caché por sitio, así que la descarga que hizo otra web no te beneficia.
Autoalojar te da además control de caché. Con nombres de archivo versionados (por ejemplo, inter-latin-v4.woff2) puedes permitirte un año:
<FilesMatch "\.woff2$">
Header set Cache-Control "public, max-age=31536000, immutable"
</FilesMatch>
La privacidad pesa igual. Cargar Google Fonts desde los servidores de Google implica que la dirección IP del visitante llega a Google. En 2022, un tribunal regional de Múnich consideró ilegal cargarlas así sin consentimiento y concedió una indemnización a un visitante. Es una sentencia alemana de primera instancia, que no vincula en España, pero avivó el debate; autoalojar evita el problema de raíz. Consúltalo con un asesor legal. Comprueba también la licencia de cada familia: las de Google Fonts suelen permitir descargar y alojar los archivos, pero conviene verificarlo.
Si te quedas con Google Fonts: pide solo los pesos que usas, mantén display=swap y añade preconnect a ambos dominios.
Una fuente de reserva que no mueva la página
El salto aparece porque la fuente de reserva y la definitiva tienen métricas distintas: ancho medio de los caracteres, altura sobre y bajo la línea base e interlineado propio. CSS permite crear una reserva "disfrazada" a partir de una fuente local y ajustarla para que ocupe casi el mismo espacio:
@font-face {
font-family: "Inter Reserva";
src: local("Arial");
size-adjust: 107%;
ascent-override: 90%;
descent-override: 22%;
line-gap-override: 0%;
}
body {
font-family: "Inter", "Inter Reserva", Arial, sans-serif;
}
size-adjust escala los glifos para igualar el ancho medio; ascent-override y descent-override fijan la altura por encima y por debajo de la línea base, y line-gap-override el interlineado intrínseco, todo como porcentaje del tamaño de fuente. Los valores del ejemplo son orientativos: calcula los de tu fuente con una herramienta como Capsize o Fontaine, o con la generación automática de algunos frameworks (Next.js la incluye). Ten en cuenta que local() depende de que esa fuente exista en el dispositivo y que la compatibilidad de estos descriptores no es idéntica en todos los navegadores: revisa MDN o Can I use. Donde no se admitan, la web funciona igual; solo vuelve el pequeño salto.
Para comprobar el resultado, bloquea la fuente web desde DevTools (en la pestaña Red, menú contextual del archivo, "Block request URL") y recarga: verás la página solo con la reserva. Compárala con la versión final. Si el titular ocupa las mismas líneas y nada se desplaza, el ajuste está bien afinado.
La alternativa radical son las pilas de fuentes del sistema: cero descargas, texto inmediato y ningún salto, porque usas la tipografía del dispositivo.
body {
font-family: system-ui, -apple-system, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif;
}
La contrapartida es que la web se ve distinta en cada sistema y tu marca pierde personalidad. Un término medio frecuente: pila del sistema para el texto largo y fuente de marca solo en los titulares, con un único archivo.
Cuántas fuentes usar y qué hacer con los iconos
Como norma, una familia y, como mucho, dos; y de cada una, dos o tres pesos (o una variable). Cada familia, peso y estilo es una petición más y un riesgo más de salto. Antes de añadir una fuente, pregúntate si el mismo diseño se logra con otro peso de la que ya tienes. Y si la cursiva solo aparece en dos palabras, quizá no merece un archivo propio.
Con los iconos pasa algo parecido. Una fuente de iconos descarga cientos de glifos aunque uses ocho; mientras no llega, verás huecos o caracteres extraños; los lectores de pantalla pueden leer símbolos sin sentido, y solo admite un color. Los SVG en línea o en un sprite, con solo los iconos que usas, pesan menos, se ven nítidos a cualquier tamaño y son más accesibles (aria-hidden="true" en los decorativos, texto alternativo en los que comunican algo).
Cómo medir y checklist final
No te fíes de la intuición: mide antes y después.
- DevTools, pestaña Red: filtra por "Font". Mira cuántos archivos hay, cuánto pesan y cuándo empiezan en la cascada: si arrancan tarde, se descubren tarde. Desactiva la caché y limita la velocidad (por ejemplo, "Slow 4G") para ver el peor caso.
- DevTools, pestaña Rendimiento: graba una recarga y localiza la marca del LCP y los saltos de diseño. En el panel Rendering, la opción "Layout Shift Regions" resalta lo que se mueve.
- PageSpeed Insights: compara los datos de campo (visitas reales) con los de laboratorio. Los primeros se agregan en una ventana de unas cuatro semanas, así que tardan en reflejar un cambio.
- WebPageTest: el filmstrip y la cascada muestran cuándo aparece el texto. Repite cada prueba varias veces y compara con y sin tus cambios.
Checklist final:
- Una o dos familias, con dos o tres pesos (o una variable).
- Solo WOFF2, autoalojado y con subconjunto para tus idiomas.
font-displaydeclarado en cada@font-face.- Una o dos fuentes críticas precargadas, con
crossoriginy la URL idéntica. - Fuente de reserva con
size-adjusty los tres overrides, o pila del sistema. - Caché larga y nombres de archivo versionados.
- Iconos en SVG, no en fuente.
- LCP y CLS medidos antes y después, en laboratorio y en campo.
Preguntas frecuentes
¿font-display: swap es siempre la mejor opción?
Es la más segura para empezar, porque el texto se ve desde el primer momento. Pero sin una reserva ajustada puede provocar saltos de diseño. Si el CLS te preocupa más que ver la fuente de marca en la primera visita, prueba optional con precarga. Es una decisión de diseño tanto como técnica, así que conviene acordarla con quien cuida la marca.
¿Debo precargar todas mis fuentes?
No. Precarga solo las que se usan en la primera pantalla, normalmente una o dos. Precargar de más compite con el CSS y la imagen principal, y puede empeorar el LCP en lugar de mejorarlo.
Ya uso Google Fonts y va bien, ¿merece la pena autoalojar?
Normalmente sí: ahorras conexiones externas, controlas caché y subconjuntos, y evitas enviar datos del visitante a un tercero. Es un cambio pequeño en la mayoría de webs: descargas los archivos, les haces el subconjunto, cambias las URLs del CSS y quitas las conexiones a los dominios de Google. Para la parte legal, consúltalo con un asesor.