Mejorando la carga del CSS

Fede Iglesias
Tiendanube Design
Published in
7 min readFeb 25, 2019

Cambios en CSS para priorizar la velocidad de carga del sitio

“¿Este es el sitio que hizo la Kessel Run en menos de 12 Parsecs?. Sí, lo es”

El CSS puede es un factor clave a la hora de “adelantar contenido” a los usuarios para que puedan ir viendo algo mientras se carga el sitio, en lugar de una pantalla en blanco que no dice nada. Si todavía no sabes bien como medir la velocidad de tu sitio, el siguiente artículo puede ayudarte:

CSS

El CSS es lo que menos impacto tiene comparado con las imágenes y el JS, pero no deja de ser importante.

Así como el JS cargado antes del HTML bloquea la carga, el CSS hace lo mismo y a diferencia del JS, que podés optar por cargarlo al final del body (evitando que sea tan bloqueante), con el CSS no se puede hacer lo mismo porque tendrías una vista del HTML sin estilos (es decir todo roto) hasta que a lo último se cargue el CSS.

Por eso tenés que pensar en CSS crítico y CSS asincrónico (no crítico), que podés cargar de forma asincrónica.

CSS Crítico

El CSS crítico es básicamente el que necesitás que si o si bloquee la carga del HTML para mostrar el contenido más relevante sin que se vea roto, por ejemplo: la navegación, el logo, un banner que esté arriba de todo en la página, una grilla de productos, etc.

Gráfico de “above the fold”, del artículo “Below the fold: What does below the fold mean?”

El CSS que consideres crítico va a ser cargado inline en el documento, ¿Porque tan desprolijo?, porque necesitás que se cargue lo más rápido posible sin tener que hacer un pedido de un archivo CSS al servidor y demorar en cargarlo. Si bien no es lo que te enseñaron (meter estilos inline) en este caso se justifica en base al objetivo que queremos lograr.

En Tiendanube tenemos este CSS insertado con Twig de la siguiente forma:

Hacer esto es lo mismo que insertarlo inline solo que queda en un archivo aparte de una forma mucho más prolija.

Si trabajás con Bootstrap, vas a tener que considerar dividirlo en 2 partes también, una crítica y una no crítica. Pensá qué elementos estás usando above the fold, (la nav, los botones, las utilities classes como pull-left, pull-right, etc) y que cosas pueden quedar en un segundo plano (modals, tables, algunos forms, etc).

Algo en lo que podés apoyarte a riesgo de mostrar contenido “roto” hasta que se cargue el resto del CSS es ocultar los elementos no críticos con una class que los mantenga invisibles hasta que tengamos todo el CSS.

Para hacer esto podés pensar en dos clases de CSS:

Por un lado agregás en tu CSS crítico las siguientes clases al final del mismo (una para ocultar elementos pero que sigan ocupando un espacio en la página y otra para directamente no se vea y no ocupe espacio):

Luego en el CSS asincrónico (te voy a contar sobre este más adelante) agregá lo siguiente, también al final:

Ahora podés agregarle cualquiera de estas clases a los elementos que no son críticos pero corren riesgo de verse rotos hasta tener todo el CSS cargado, por ejemplo puede ser el footer de la página haciendo que se vea lo siguiente:

A la izquierda el footer oculto pero ocupando un espacio y a la derecha el mismo footer ya con su CSS listo para ser mostrado

Y evitás mostrar esto hasta que no este listo su CSS

Así se vería el footer si no usaras las clases de visible-when-content-ready o display-when-content-ready

CSS asincrónico

Este CSS básicamente es todo lo que no considerás como crítico, debería ser bastante ya que es todo el CSS que estás removiendo para no bloquear tanto tiempo la carga de la página.

¿Cómo cargás el CSS asincrónico? Usá el plugin llamado loadCss hecho con Javascript puro y te permite “demorar” la carga de CSS que no necesitás de inmediato. La carga la va a definir de la misma forma que lo decide el navegador con el tag de javascript async.

Para usar este plugin simplemente en el tag head del HTML llamálo de la siguiente forma:

<script>!function(a){"use strict";var b=function(b,c,d){function j(a){return e.body?a():void setTimeout(function(){j(a)})}function l(){f.addEventListener&&f.removeEventListener("load",l),f.media=d||"all"}var g,e=a.document,f=e.createElement("link");if(c)g=c;else{var h=(e.body||e.getElementsByTagName("head")[0]).childNodes;g=h[h.length-1]}var i=e.styleSheets;f.rel="stylesheet",f.href=b,f.media="only x",j(function(){g.parentNode.insertBefore(f,c?g:g.nextSibling)});var k=function(a){for(var b=f.href,c=i.length;c--;)if(i[c].href===b)return a();setTimeout(function(){k(a)})};return f.addEventListener&&f.addEventListener("load",l),f.onloadcssdefined=k,k(l),f};"undefined"!=typeof exports?exports.loadCSS=b:a.loadCSS=b}("undefined"!=typeof global?global:this);</script>

Y justo debajo haces los llamados asincrónicos a nuestro CSS;

<script id="style-scss">loadCSS( '{{ 'style.scss.tpl' | static_url }}', document.getElementById("style-scss"));</script>

Recordá que la ruta del ejemplo anterior está armada con Twig pero podés agregar la ruta que necesites. Es importante que coincidan el id del tag script con el que usas en el getElementByID, porque lo que va a hacer el script es insertar justo antes del tag script con el id “X” (en el DOM), ese mismo CSS con ese id “X”. En este caso el id es “style-css”.

Este mismo plugin lo usamos en Tiendanube para cargar el CSS de Font Awesome de forma asincrónica y evitando la carga de una tipografía que puede llegar a ser muy pesada (aún en su versión 5 cargándola con JS defer, es mejor cargarla como CSS asincrónico). En el ejemplo justo antes del “style-css” está el llamado a Font Awesome (está antes por si necesitás pisar algunas clases de Font Awesome con nuestro CSS)

<script id="font-awesome-css">loadCSS( '{{ 'font-awesome/font-awesome-5.css' | static_url }}', document.getElementById("font-awesome-css"));</script>

En nuestras tiendas tenemos el CSS divido, como vimos anteriormente (asincrónico y crítico), pero también tenemos un SASS con los colores y tipografías guardados en la personalización de cada tienda. Hasta el momento ese SASS es cargado de la forma convencional bloqueando la carga del documento a propósito para asegurarnos de tener todos los colores y tipografías aplicados antes de mostrar algo y evitando un salto visual en estos estilos específicos (si los aplicáramos como algo asincrónico).

Fonts

En las fonts hay mucho potencial para mejorar la velocidad. Los cambios que aplicamos en las tiendas estuvieron relacionados a:

  • Llamar solo las fonts que fueron guardadas en la configuración en lugar de llamar a todas en el documento. Llamar más de 3 o 4 fonts distintas (aún siendo de google fonts) puede comenzar perjudicar la velocidad de carga, por eso siempre mantengamos la cantidad de fonts al mínimo en nuestro sitio.
  • Usar solo los pesos de fonts que necesitamos sin cargar de más, por lo general son 300, 400 y 700.
  • Llamar las fonts de Google con un display: swap; , evitando que las fonts bloqueen la carga del sitio mientras son cargadas y haciendo que mientras tanto se muestre una font de fallback o default.
    Esto puede generar un pequeño salto entre la font inicial y la final pero adelanta el tiempo en que se puede mostrar texto para que el usuario ya pueda consumir el contenido.

En el ejemplo anterior cargamos todas las fonts si el usuario esta en la página de configuración de su tienda ya que las fonts se cambian en tiempo real a medida que se personaliza el diseño y necesitamos tener todo cargado. Por otro lado (en el else) cargamos solamente las fonts relacionadas a cada configuración o cada “setting”.

Por otro lado así como vimos que usando loadCSS se puede cargar CSS de forma asincrónica, también podés cargar fonts de forma asincrónica pero esto va a dar como resultado lo se llama FOUT (Flash of Unstyled Text), es decir que por un momento se vera el sitio con las tipografías default (Arial, Helvética, etc) que definas en el CSS, sans serif o con serif; y luego se cargará el CSS con las tipografías finales. Esto puede mejorar la velocidad de carga pero va a generar un salto visual a nivel tipográfico.

Para más información sobre FOUT (y otros casos como FOIT y FOFT) te dejo este artículo que explica sobre el tema.

Por el momento en Tiendanube no implementamos ninguna de estas estrategias pero cualquier experiencia con esto pueden compartirla =)

¿Qué sigue?

Si ya pudiste optimizar todo lo que tiene que ver con CSS te recomiendo seguir leyendo los cambios que aplicamos sobre los otros dos frentes para mejorar la velocidad:

Gracias por tomarte este rato e interesarte en mejorar la velocidad de carga, espero que te haya servido y haber aportado un granito de arena a mejorar la experiencia en tus sitios.

¡Gracias también a Bruno Navello y Seba Marcó! Product designers que la rompen en Tiendanube y trabajaron conmigo en mejorar la velocidad.

Para más contexto de como mejoramos la velocidad en en nuestras tiendas Tiendanube, les recomiendo leer:

[Actualización 09/2020]

Este artículo muestra mejoras de performance previas a la actualización de Google Lighthouse 6 durante Mayo del 2020. Si bien siguen vigentes, hubo nuevos aprendizajes que podés ver este otro artículo. 👇

--

--