¿Cómo optimizar las fuentes de una web?

Ramón Saquete

Escrito por Ramón Saquete

Optimizar fuentes en el sitio WebCuantas más fuentes tenemos en una web, peores son las métricas de WPO y más difícil es optimizarla para obtener un buen rendimiento. Si queremos tener un buen equilibrio entre rendimiento y diseño, siempre debemos prestar especial cuidado a la optimización de las fuentes y evitar un uso abusivo.

A continuación exponemos varias técnicas de optimización básicas que se deberían aplicar siempre. También explicaremos técnicas de optimización avanzadas que no siempre es recomendable aplicar, por lo que conviene medir si su efecto es positivo o negativo en cada situación, especialmente cuando se añade código JavaScript adicional.

Técnicas básicas de optimización de fuentes

Usar el mínimo número de fuentes posibles

Hay que intentar homogeneizar el diseño usando el mínimo número de fuentes posible, siendo el ideal para el rendimiento cero.

Si nos preocupa el rendimiento de nuestro sitio, por encima del diseño o la imagen de marca, lo ideal es optimizar la descarga y el pintado, usando directamente la fuente del sistema operativo del usuario. De esta manera evitamos que el navegador deba descargar la fuente. Para implementar esto, simplemente debemos darle a la propiedad «font-family» el valor «serif»,»sans-serif» o «monospace» en las reglas de CSS, con lo que cualquier navegador asignará la fuente predeterminada del sistema acorde con el estilo elegido. Si queremos coger la fuente del sistema más agradable posible, para asegurar una buena legibilidad, podemos establecer una pila de fuentes como esta:

   font-family: -apple-system, 
                BlinkMacSystemFont, 
                "Segoe UI", 
                Roboto, 
                Oxygen-Sans, 
                Ubuntu, 
                Cantarell, 
                "Helvetica Neue", 
                sans-serif;

El navegador mostrará la primera fuente disponible en el sistema operativo de esta pila. Esta lista se puede quedar desactualizada, ya que los sistemas operativos cambian, pero no es demasiado problema porque siempre se utilizará la fuente más general, que debe estar al final de la pila.

Es recomendable evitar cargar fuentes cuando su uso es mínimo y así como para aquellos casos en que si no especificamos la fuente para negrita o cursiva, y en el CSS le decimos que queremos verla en alguno de estos formatos, el propio navegador las convierte razonablemente bien.

También se deben eliminar del CSS las referencias a fuentes o variantes de ellas que no se utilicen. Las variantes pueden ser: cursiva, negrita fuerte, negrita normal, cursiva y negrita normal, etc. Si la fuente no se emplea o no está visible, no se descargará, pero es preferible acortar el código CSS todo lo posible.

Cachear las fuentes en el navegador

No olvides cachear las fuentes en el navegador con la cabecera cache-control.

Quitar los subconjuntos de glifos que no se usan

Se deben eliminar de las fuentes los glifos que no pertenezcan al alfabeto que utiliza la web. Por ejemplo, en caso de ser una web en inglés y español los glifos del alfabeto latino son suficientes, por lo que no necesitamos caracteres chinos, japoneses, rusos o árabes.

Para ello modificaremos la fuente con un «subsetter«. Con esta herramienta seleccionamos los subconjuntos de glifos que queremos utilizar. Como ejemplo, aquí tenéis una online https://everythingfonts.com/subsetter y otra por línea de comandos https://github.com/fonttools/fonttools.

Una vez eliminado el conjunto de caracteres que no se van a utilizar y como prevención, conviene especificar al navegador, en la declaración de la fuente, que no debe descargar ni aplicar esa fuente a caracteres ajenos al subconjunto elegido con la propiedad «unicode-range«.

A continuación, vemos un ejemplo para los caracteres que pueden aparecer en español:

@font-face {
  font-family: 'fuente';
  src: url('fuente.woff2') format('woff2');
  unicode-range: U+0000-00FF; 
}

Podemos ver la lista de rangos Unicode aquí:
https://en.wikipedia.org/wiki/Unicode_block

Si estamos cargando la fuente desde un enlace externo de Google fonts, podemos especificar el subconjunto escogido en los parámetros de la URL de Google, añadiendo la siguiente QueryString:

subset=cyrillic

Sin embargo, Google no siempre tiene disponible el subconjunto exacto que le pedimos y puede que devuelva una fuente más grande de lo necesario, por lo que recomiendo siempre descargarla y optimizarla manualmente.
El subconjunto de los caracteres latinos, siempre se carga por defecto, por lo que no es necesario especificarlo con subset=latin.

Utilizar el formato WOFF2

De los cinco formatos existentes de fuentes (WOFF2, WOFF, EOT, TTF y SVG), el formato WOFF2, desarrollado por Mozilla, es el más recomendable, ya que actualmente es compatible con todos los navegadores modernos y no necesita compresión adicional. No obstante, para dar soporte a navegadores antiguos se puede añadir la fuente en el resto de formatos.

Los formatos EOT, TTF y SVG siempre es mejor enviarlos comprimidos desde el servidor (preferiblemente con Brotli q11). El formato WOFF sólo necesita la compresión a veces, dependiendo del caso y cuando no se han usado las opciones de compresión óptimas para generar el archivo.

Para generar una fuente en distintos formatos podemos utilizar herramientas onlines como Font Squirrel o www.font-converter.net.

Establece el orden de carga correcto de los distintos formatos de fuente

Cuando queremos dar soporte a navegadores antiguos, se debe llevar cuidado con el orden en el que se añaden las fuentes en el CSS, ya que el navegador empleará el primer archivo compatible que encuentre, aunque no tenga la mejor compresión. Por eso, el orden correcto sería WOFF2, WOFF, EOT, TTF y SVG. EOT debe estar antes que TTF, ya que Internet Explorer también es compatible con TTF pero solo parcialmente:

@font-face {
       font-family: 'fuente';
       src: url('fuente.woff2') format('woff2'), /* todos los navegadores modernos */ 
       url('fuente.woff') format('woff'), /* navegadores que no se han actualizado */
       url('fuente.eot'), /* IE9 */
       url('fuente.eot?#iefix') format('embedded-opentype'), /* IE9 */
       url('fuente.ttf')  format('truetype'), /* Safari, Android, iOS */
       url('webfont.svg#svgFontName') format('svg'); /* Safari version <= 4.1  */
}

Si no queremos dar soporte a navegadores antiguos, solo con el formato WOFF2 es suficiente. Al fin y al cabo, si no se carga la fuente personalizada se cargará la del sistema.

Visualización de fuente del sistema antes de cargar el contenido

En este punto se evita el efecto llamado FOIT (Flash of Invisible Text), consistente en que durante la carga de la página, el usuario no puede ver los textos de la web hasta que ha terminado de cargar la fuente. Para lograr evitar el FOIT, debemos usar dentro de la regla @font-face la siguiente declaración CSS:

font-display:swap;

De esta forma el navegador mostrará la fuente del sistema, mientras carga la fuente de la web cambiando el efecto FOIT por un efecto FOUT (Flash of Unstyled Text), que consiste en que el usuario ve cómo el estilo de la fuente cambia de golpe. Este efecto tampoco es deseable pero es preferible a que el usuario no pueda ver nada.

Efectos FOIT y FOUT
Efectos FOIT y FOUT

Actualmente muy pocos navegadores hacen caso de la declaración font-display:swap, pero la alternativa es usar JavaScript adicional para evitar el FOIT, con lo que la sobrecarga que implica la ejecución de este JavaScript puede disminuir el rendimiento.

De todas formas en el último punto explico esta técnica y recomiendo aplicarla sólo si se realizan pruebas, para ver en cada caso concreto si el rendimiento mejora o empeora.

Si estamos cargando la fuente desde una URL de Google fonts, podemos aplicar esta declaración de CSS añadiendo el siguiente párametro a la QueryString de la URL:

display=swap

Veamos un ejemplo de URL de Google Fonts, pasando los parámetros para evitar el efecto FOIT y seleccionando el subconjunto de caracteres latinos:

https://fonts.googleapis.com/css?family=Cabin:400,700&display=swap&subset=latin

Técnicas avanzadas de optimización de fuentes

Adelantar la carga de las fuentes que se usan en el above the fold (pero no demasiado)

Podemos minimizar el efecto FOUT y FOIT (en los navegadores que no permiten el uso de «font-display:swap»), adelantando la carga de las fuentes críticas, es decir, aquellas que se muestran nada más cargar la página. Para ello, las fuentes críticas se pueden incluir dentro de una etiqueta <style> o utilizando una etiqueta link con las propiedades rel=»preload» y as=»font» dentro del HTML. Ejemplos:

<style>
@font-face {
  font-family: 'fuente';
       src: url('fuente.woff2') format('woff2');
}
/* Aquí debe ir el resto del CSS crítico para que esta técnica de resultado */</style>

 

O metemos el código de arriba en un CSS externo y precargamos la fuente desde el HTML así:

<link rel="preload" href="/fonts/fuentes.woff2" as="font">

 

De esta forma el árbol de dependencias sólo tiene que realizar un salto para llegar a la fuente (HTML → fuente), en lugar de dos (HTML → CSS → fuente). Sin embargo, la etiqueta <link> es mejor si estamos seguros de que la fuente se va a utilizar siempre en el above the fold, ya que incluyendo el CSS con la etiqueta <style>, la descarga no se produce hasta que el navegador encuentra alguna regla de CSS que se aplique al HTML y utilice dicha fuente.

Dentro de los recursos críticos, hay algunos más críticos que otros y adelantar la carga de la fuente para minimizar el FOUT no es tan importante como que se carguen primero los estilos del above the fold. Así que adelantar la fuente demasiado incluyéndola como un DATA URI (con lo que el HTML y la fuente se descargan a la vez), se considera un anti-patrón, ya que la fuente dejaría de ser cacheable y retrasaría la carga del resto de recursos críticos. Tampoco suele ser buena idea adelantar su carga con HTTP/2 server push.

Retrasar la carga de las fuentes que no se muestran en el above the fold

Las fuentes no empiezan a descargarse hasta que el navegador no detecta que existe alguna regla de CSS que utiliza dicha fuente. Pero si son fuentes que sólo se usan en el below the fold (por ejemplo, una fuente exclusiva para el pie de página), se descargarán pudiendo interferir con la descarga de recursos críticos, por lo que recomiendo empezar a descargarlas de forma diferida. Para eso se pueden cargar desde un CSS que contenga todos los estilos no críticos cuya carga retrasada se puede implementar así:

    <noscript id="deferred-styles">
      <link rel="stylesheet" type="text/css" href="/bundle-css-no-critico-v452372.css"/>
    </noscript<
    <script defer src="bundle-js-v23423.js"></script>

Dentro del JavaScript tendremos las siguientes líneas, que se ejecutaran después de cargar la página entera (debido al atributo defer) y una vez el navegador está listo para realizar un repintado de la pantalla:

      var loadDeferredStyles = function() {
        var addStylesNode = document.getElementById("deferred-styles");
        var replacement = document.createElement("div");
        replacement.innerHTML = addStylesNode.textContent;
        document.body.appendChild(replacement)
        addStylesNode.parentElement.removeChild(addStylesNode);
      };
      var raf = window.requestAnimationFrame || window.mozRequestAnimationFrame ||
          window.webkitRequestAnimationFrame || window.msRequestAnimationFrame;
      if (raf){ 
         raf(function() { window.setTimeout(loadDeferredStyles, 0); });
      }
      else{ 
         window.addEventListener('load', loadDeferredStyles);
      }

 

Evitar el FOIT y los repintados múltiples con JavaScript

Caja de tiposCada vez que se carga una fuente, el navegador realiza un repintado con la nueva fuente. Si son varias, este repintando puede ocurrir varias veces. Si este es nuestro caso, recomiendo usar la librería de JavaScript FontFaceOBserver que podéis descargar aquí y que usa por debajo la API de JavaScript Font Loading en aquellos navegadores que la soportan.

Esta librería nos permite implementar múltiples estrategias en la carga de fuentes, pero lo más importante es que nos permite forzar y observar la carga de fuentes existentes en el CSS y asociar código al momento en que se han terminado de cargar un conjunto de fuentes.

De esta forma podemos cargar las fuentes sin necesidad de que exista una regla de CSS asociada y, una vez cargadas, cambiar una clase en el HTML que haga que se muestren todas a la vez.

Ejemplo:

Código CSS:

@font-face {
  font-family: 'Mi fuente';
  src: url(mi-fuente.woff2) format('woff2'),
       url(mi-fuente.woff) format('woff');
  unicode-range: U+0000-00FF;
  /* podemos especificar font-display:swap; para que las herramientas de WPO de Google nos puntuen mejor, pero no servirá de nada ya que lo vamos a implementar con JavaScript */}
@font-face {
  font-family: 'Mi fuente';
  src: url(mi-fuente-italica.woff2) format('woff2'),
       url(mi-fuente-italica.woff) format('woff');
  font-style: italic;
  unicode-range: U+0000-00FF;
}

/* Fuente del sistema que se usará mientras se carga la página */html{
   font-family: -apple-system, 
                BlinkMacSystemFont, 
                "Segoe UI", 
                Roboto, 
                Oxygen-Sans, 
                Ubuntu, 
                Cantarell, 
                "Helvetica Neue", 
                sans-serif;
}

/* Fuente que se usa cuando ya está cargada la fuente */html.fonts-loaded{
   font-family: 'Mi fuente';
}

html.fonts-loaded em{
   font-style: italic;
}

 

Código JavaScript:

var normal = new FontFaceObserver('Mi fuente');
var italic = new FontFaceObserver('Mi fuente', {
  style: 'italic'
});
var html = document.documentElement;

//console.log('cargando fuentes');
if (sessionStorage.fontsLoaded) {
    document.documentElement.classList.add('fonts-loaded')
} 
else {
  Promise.all([
  normal.load(),
  italic.load()
  ]).then(function () {
    //console.log('fuentes cargadas');
    html.classList.add('fonts-loaded');
    sessionStorage.fontsLoaded = true;
  }).catch(function () {
    //console.log('fallo cargando fuentes');
    sessionStorage.fontsLoaded = false;
  });
}

En el ejemplo guardamos una variable en el objeto sessionStorage que nos sirve como pista, para saber si las fuentes están con mayor probabilidad en la cache y así cargarlas directamente, sin necesidad de descargarlas de nuevo.

Conclusión

Cuantas más fuentes tenemos, la carga es menos eficiente y la optimización es más compleja. No utilizar fuentes o utilizar como máximo una es la mejor estrategia para ahorrar costes de desarrollo y mejorar el rendimiento, a costa de sacrificar algo del diseño que en ocasiones puede ser poco relevante.

Ilustración de Willi Heidelbach, CC BY 2.5, Enlace

  •  | 
  • Modificado el
Ramón Saquete
Ramón Saquete
Desarrollador web y consultor SEO técnico en Human Level. Graduado en Ingeniería Informática e Ingeniería Técnica en Informática de Sistemas. También es Técnico Superior en Desarrollo de Aplicaciones Informáticas y posteriormente obtuvo la Certificación de Aptitud Pedagógica. Experto en WPO e indexabilidad.

2 comentarios

¿Y tú qué opinas? Deja un comentario

Por si acaso, tu email no se mostrará ;)

  1. Hola Ramón, es muy bueno el nivel técnico del artículo y todos los aspectos que se mencionan. Felicidades por este pedazo de artículo, pero me surge una duda. Cuando estamos tratando de optimizar las peticiones de nuestro sitio web al servidor y utilizamos Google Fonts, ¿podríamos posponer de alguna forma la petición a esta fuente y acelerar el proceso de carga de nuestro sitio web?

    La típica petición que se hace a «https://fonts.googleapis.com/css?family=…» ¿Me explico? Espero que sí, es algo que en Page Speed siempre sale la típica recomendación de «Elimina los recursos que bloqueen el renderizado».

    Saludos, Joel

    1. Hola Joel,
      Gracias por tu comentario.
      Mi recomendación con las fuentes cargadas desde Google fonts, en el caso de que sean fuentes no críticas (no se usan en el above the fold), lo mejor es cargarlas con la misma técnica que se explica en el post para las fuentes normales. Si por el contrario, son fuentes críticas, lo mejor es descargarlas de Google fonts para poder adelantar su carga desde el servidor de la web, utilizando la etiqueta «<link rel=»preload» …». Si no se hace así corremos el riesgo de que Google cambie el nombre del archivo y no se descarguen.

      Saludos

Entradas relacionadas

es