FlowingDev

Font Fallbacks, explicados: esquivando el terremoto de layout del CLS

Aprende cómo las métricas de fuentes en CSS como size-adjust y ascent-override previenen el Cumulative Layout Shift haciendo que las fuentes del sistema coincidan perfectamente con tus web fonts.

Probar la herramienta: Generador de Font Fallback (CLS)

En una oración

Las métricas de font fallback son un conjunto de superpoderes de CSS que estiran y apachurran artificialmente una fuente del sistema para que ocupe exactamente el mismo espacio matemático que tu web font personalizada, evitando que el layout de la página salte violentamente cuando la fuente por fin termina de cargar.

El problema que resuelve

Para entender el problema, primero tienes que entender la guerra tipográfica más antigua de la web: ¿qué le mostramos al usuario mientras esperamos a que se descargue un web font personalizado de 150KB en una conexión 3G inestable?

Históricamente, los navegadores tomaban uno de dos caminos, y ambos eran terribles. El primero era FOIT (Flash of Invisible Text), donde el navegador simplemente ocultaba el texto hasta que la fuente se descargara. Los usuarios se quedaban mirando una pantalla en blanco, preguntándose si tu sitio estaba roto. El segundo era FOUT (Flash of Unstyled Text), donde el navegador renderizaba el texto inmediatamente usando una fuente local del sistema (como Arial o Times New Roman), y luego la intercambiaba a la carrera por tu hermosa fuente personalizada en el milisegundo en que llegaba.

La comunidad de desarrollo web llegó a la conclusión de que FOUT es mejor para la accesibilidad y el performance percibido. Después de todo, leer un texto feo es mejor que no leer ningún texto. Formalizamos esto con la regla de CSS font-display: swap.

Pero font-display: swap dio a luz a un nuevo monstruo: el Cumulative Layout Shift (CLS).

Cada fuente tiene un esqueleto único. Una "x" en Roboto es físicamente más ancha que una "x" en Arial. La altura de las letras mayúsculas varía. El espaciado que viene integrado en los archivos de las fuentes es completamente diferente. Cuando el navegador quita Arial para poner Roboto, el texto de repente ocupa una cantidad diferente de espacio horizontal y vertical. Los párrafos hacen reflow. El texto salta a una nueva línea.

Si esto pasa mientras el usuario ya está leyendo o a punto de hacer clic en un botón, la página entera salta como si un terremoto acabara de golpear el DOM. Los botones se mueven de debajo del cursor del usuario. Las imágenes son empujadas hacia abajo. Google introdujo las métricas de Core Web Vitals para medir esta exacta frustración, penalizando fuertemente el SEO de los sitios con un CLS alto. Necesitábamos una forma de hacer que la fea fuente temporal del sistema imitara a la perfección la huella física de la hermosa fuente personalizada.

Cómo funciona tras bambalinas

Para detener el layout shift, no cambiamos la web font personalizada. En su lugar, creamos una versión "Frankenstein" de una fuente local del sistema usando la regla @font-face. Usamos descriptores especializados de CSS para sobrescribir la geometría interna de la fuente del sistema.

La anatomía de la caja de una fuente

Para manipular una fuente, necesitas entender cómo calcula un navegador la caja alrededor de una línea de texto. No se trata solo del font-size. Depende de tres métricas internas que vienen "horneadas" en el propio archivo de la fuente:

  1. Ascent: La distancia desde el baseline invisible (donde se asientan las letras) hasta la cima de los caracteres más altos (como la parte superior de una 'h' o una 'T').
  2. Descent: La distancia desde el baseline hasta el fondo de los caracteres que cuelgan más abajo (como la cola de una 'y', 'g' o 'p').
  3. Line Gap: El espacio vacío recomendado entre el descent de una línea y el ascent de la siguiente línea (a menudo llamado leading o interlineado).

En CSS, la altura total de una línea de texto es aproximadamente ascent + descent + line gap. Si tu fuente personalizada y tu fuente del sistema tienen valores diferentes para estas tres métricas, tu layout saltará verticalmente. Si las letras tienen anchos diferentes, tu layout saltará horizontalmente.

Entran los Overrides

El módulo de CSS Fonts Level 4 introdujo cuatro descriptores mágicos que se pueden aplicar dentro de un bloque @font-face. Estos descriptores interceptan la lectura que hace el navegador de las métricas internas del archivo de la fuente y las sobrescriben al vuelo.

  • size-adjust: Esto actúa como un multiplicador para toda la fuente. Si tu fuente personalizada es por lo general más ancha que Arial, podrías poner size-adjust: 110% en Arial para estirarla horizontalmente.
  • ascent-override: Sobrescribe la métrica interna de ascent de la fuente.
  • descent-override: Sobrescribe la métrica interna de descent de la fuente.
  • line-gap-override: Sobrescribe la métrica interna de line gap de la fuente.

Juntando las matemáticas

El truco está en que size-adjust escala todo, incluyendo los anchos horizontales, el ascent y el descent. Así que, si escalas una fuente del sistema un 110% para hacer que los anchos de las líneas coincidan con tu web font, inadvertidamente has hecho que la altura vertical de la fuente del sistema sea un 110% más alta, ¡lo cual causa un layout shift vertical!

Para arreglar esto, los overrides (ascent-override, descent-override, line-gap-override) se calculan como porcentajes del valor de size-adjust, no del font size original. Usas size-adjust para igualar perfectamente el ancho horizontal, y luego usas las métricas de override para básicamente apachurrar la altura vertical hacia abajo (o estirarla hacia arriba) para que coincida con el bounding box exacto de tu fuente personalizada.

Así es como se ve el código resultante. Nota cómo creamos una familia de fuentes completamente nueva e imaginaria llamada Roboto-Fallback que envuelve a la fuente local Arial con overrides matemáticamente precisos:

/* 1. Carga tu web font real */
@font-face {
  font-family: 'Roboto';
  src: url('/fonts/roboto.woff2') format('woff2');
  font-display: swap;
}

/* 2. Crea el fallback Frankenstein */
@font-face {
  font-family: 'Roboto-Fallback';
  src: local('Arial');
  /* Estira Arial horizontalmente para que coincida con Roboto */
  size-adjust: 104.5%; 
  /* Apachurra las métricas verticales de Arial para que coincidan con Roboto */
  ascent-override: 89.92%;
  descent-override: 23.44%;
  line-gap-override: 0%;
}

/* 3. Úsalas juntas en tu CSS */
body {
  font-family: 'Roboto', 'Roboto-Fallback', sans-serif;
}

Cuando la página carga, el navegador pinta al instante Roboto-Fallback. Es solo la Arial local, pero estirada horizontalmente y apachurrada verticalmente para coincidir con la huella matemática exacta de Roboto. Cuando el archivo real de Roboto termina de descargarse, ocurre el swap, la forma de las letras cambia de diseño, pero el layout no se mueve ni un solo pixel.

Historias del mundo real

La trampa del botón "Cancelar"

Una gran plataforma de e-commerce estaba sufriendo con carritos abandonados y tickets de soporte de clientes furiosos. Los usuarios se quejaban de que vaciaban sus carritos por accidente. El equipo de ingeniería grabó sesiones de usuarios y encontró al culpable. El sitio cargaba una fuente de marca bastante pesada. El usuario veía el botón de "Checkout" justo al lado del botón de "Vaciar Carrito". Justo en el instante en que hacían clic en "Checkout", cargaba la fuente de la marca. Esta fuente era significativamente más ancha que la predeterminada del sistema. El texto hacía reflow, empujando el botón de "Checkout" hacia la derecha y posicionando el botón de "Vaciar Carrito" exactamente debajo del clic descendente del mouse del usuario. Implementar size-adjust y los metric overrides fijó los botones en su lugar durante el font swap, tirando de inmediato la tasa de cancelaciones accidentales.

El terremoto del artículo de noticias

Un popular blog de tecnología pasó por un rediseño masivo, con todo y una hermosa y moderna fuente serif para lecturas largas. Para priorizar la velocidad, usaron font-display: swap. Sin embargo, los lectores con conexiones más lentas experimentaron un fenómeno muy brusco: cargaban un artículo, scrolleaban tres párrafos hacia abajo y empezaban a leer. Dos segundos después, cargaba la fuente serif. Como la fuente serif tenía un ascent y descent más grandes que la default del sistema, cada una de las líneas de texto crecía por una fracción de milímetro. A lo largo de tres párrafos, esto se acumulaba formando un salto vertical masivo, mandando la oración que el usuario estaba leyendo completamente fuera de la pantalla. Al agregar un ascent-override y descent-override a una fuente fallback local, el texto hizo swap perfectamente en su lugar, manteniendo el ojo del lector exactamente donde debía estar.

La decepción del score de Lighthouse

Un desarrollador frontend pasó tres semanas optimizando una web app para los Core Web Vitals de Google. Comprimió imágenes en WebP, aplicó defer al JavaScript no crítico y minificó su CSS. Corrió Google Lighthouse y esperaba con confianza un score perfecto de 100. En lugar de eso, sacó un 75. El reporte marcó una penalización masiva por Cumulative Layout Shift. Todo el salto lo causaba un simple titular <H1> que pasaba de una línea a dos líneas cuando cargaba la web font personalizada. En lugar de rediseñar el header o depender de texto invisible, el desarrollador generó métricas de fallback exactas para la fuente del encabezado. El layout shift cayó a cero, el score de Lighthouse llegó a 100 y el desarrollador por fin pudo dormir.

Errores comunes y trampas

  • Adivinar los porcentajes: No puedes calcular estos números a ojo. Los cálculos requieren extraer exactamente las unidades UPM (Unique Units Per Em), ascent, descent y los datos del line gap directamente desde las tablas binarias dentro de los archivos, tanto de la web font como de la fuente del sistema, y hacer las multiplicaciones cruzadas. Usa siempre un generador programático para hacer bien las matemáticas.
  • Aplicar overrides a la web font: Una trampa clásica es agregar size-adjust directamente a la declaración @font-face de la web font personalizada. Esto no logra nada. Los overrides deben aplicarse a una declaración @font-face completamente separada que cargue una fuente del sistema local(), la cual luego colocas en segundo lugar en tu stack de font-family.
  • Elegir fallbacks estructuralmente opuestos: Las métricas de las fuentes solo pueden hacer magia hasta cierto punto. Si tu fuente personalizada es una sans-serif ultra condensada, y usas una fuente serif ancha y extendida como tu base para el fallback local, la matemática de size-adjust hará que la fuente serif se vea completamente distorsionada e ilegible mientras espera para hacer el swap. Siempre elige una fuente local del sistema que tenga una estructura esquelética más o menos similar a tu web font.
  • Olvidarse del line-gap-override: Los devs seguido se enfocan por completo en el ancho y el alto y asumen que poner el line gap en normal u omitirlo está bien. Si el navegador decide agregar un leading por defecto a la fuente fallback que no coincida perfectamente con el leading interno de la web font, tu ritmo vertical saltará. Defínelo siempre de forma explícita.

Por qué debería estar en tu radar

Deberías pensar en las métricas de font fallback desde el momento en que decides usar una web font personalizada en cualquier proyecto donde la experiencia de usuario y el SEO importen.

Históricamente, la tipografía en la web era un compromiso entre la estética (usar fuentes bonitas) y el performance (evitar layouts que saltan). Con la llegada de las métricas de fuentes en CSS, ese compromiso está muerto. Cada vez que estés auditando los Core Web Vitals de un sitio, peleando con un score alto de CLS, o construyendo componentes de UI reutilizables que no pueden darse el lujo de temblar en la pantalla, los overrides de font fallback son la solución definitiva de ingeniería. Transforman a las web fonts de ser un riesgo de carga a una transición fluida e imperceptible.

Profundiza

  • MDN Web Docs: size-adjust - Documentación oficial de Mozilla sobre cómo el descriptor size-adjust escala los contornos de los glifos y las métricas.
  • MDN Web Docs: ascent-override - Análisis profundo sobre cómo sobrescribir la métrica de ascent de la fuente.
  • web.dev: Improved font fallbacks - Excelente guía de Google sobre el uso de estos exactos descriptores de CSS para arreglar el Cumulative Layout Shift.
  • W3C CSS Fonts Module Level 4 - La especificación oficial detallando las matemáticas exactas y las reglas de renderizado de los navegadores para los overrides de métricas de fuentes.

Teoría lista. Hora de ensuciarse las manos — 100% en tu navegador.

Probar la herramienta: Generador de Font Fallback (CLS)