La naturaleza de CSS (sobre todo cuando no se usan estrategias de organización o arquitecturas) unida a los frenéticos ritmos de desarrollo, suelen provocar que los desarrolladores adquieran muy malas prácticas que hacen que el código CSS se vuelva muy difícil de mantener.

PurgeCSS: Código CSS no utilizado

¿Qué es el código muerto?

A medida que van creciendo nuestros proyectos y realizamos modificaciones en los elementos HTML y estructuras de páginas de nuestro proyecto, es muy habitual que el código CSS permanezca y no se borre del proyecto (el clásico «lo dejo por si acaso»), generando el denominado código CSS muerto (código CSS no utilizado).

El código CSS muerto engorda el tamaño de los ficheros de CSS, no es de utilidad y, por si fuera poco, dependiendo de la especificidad y nombrado de elementos CSS es muy posible que introduzca potenciales errores. Hay que intentar ser muy organizado y evitarlo.

¿Cómo detecto código muerto?

En pequeños proyectos puede resultar sencillo (incluso podemos revisarlo manualmente), pero en proyectos grandes detectar código muerto puede ser una tarea muy complicada, sobretodo si utilizamos enfoques de CSS global sin una arquitectura concreta.

Uno de los mejores métodos para prevenir la creación de código muerto que mejor funcionan hoy en día, es crear componentes, ya que vuelve mucho más fácil mantener código HTML, CSS y Javascript asociado a estructuras relacionadas.

Una de las formas rápidas de visualizar el código CSS o Javascript que está siendo utilizado al navegar por una página, es la característica CSS/JS Coverage (Cubrimiento CSS/JS) de Google Chrome.

Para acceder a esta sección, pulsamos CTRL+SHIFT+I y accedemos al Inspector de Elementos. El menú de los tres puntos, seleccionamos More tools / Coverage. Nos aparecerá un nuevo panel, donde deberemos pulsar el botón para volver a recargar la página utilizando esta funcionalidad.

Google Chrome: Coverage

Observaremos que nos aparecen una lista de archivos (CSS y Javascript) junto a unas franjas donde se nos muestra en rojo la cantidad de código no utilizado y en azul el código utilizado.

Hay que tener mucho cuidado con esto, ya que el navegador va actualizando esta información a medida que usamos la página (scroll, botones, acciones...), por lo que es muy probable que haya código que se va «descubriendo» y reflejando a medida que lo utilizamos.

Eliminación de código CSS muerto

Sin embargo, aunque la funcionalidad Coverage es una forma genial de hacernos una idea de si algún fragmento de código se utiliza al realizar una acción en una parte concreta de nuestra web, probablemente nos gustaría alguna forma un poco más automática de eliminar código muerto de nuestra página.

Existen varias herramientas que analizan el código HTML/JS de nuestro proyecto y luego comparan con el CSS en busca de código CSS no utilizado. Esto es especialmente útil cuando se utilizan frameworks pesados que tienen gran cantidad de reglas que muchas veces no se utilizan.

Los plugins de eliminación de código CSS no utilizado más populares son los siguientes:

Plugin Autor Descripción
PurgeCSS FullHuman Elimina código CSS no utilizado. Plugins para PostCSS, Webpack, Gulp, Grunt y Gatsby.
UnCSS uncss Elimina código CSS no utilizado. Usa PostCSS. Plugins para Grunt, Gulp y Broccoli.
PurifyCSS purifycss Elimina código CSS no utilizado. Plugins para Grunt, Gulp y Webpack.

La herramienta que parece tener mejor soporte y que más se actualiza es PurgeCSS, que es la que utilizaremos en este artículo.

Instalación de PurgeCSS

Para instalar y configurar PurgeCSS, primero debemos instalar el paquete en nuestro proyecto, como dependencia de desarrollo:

$ npm install --save-dev postcss-purgecss

Una vez hecho esto, editaremos nuestro fichero de configuración postcss.config.mjs y añadimos el plugin. Generalmente, purgecss es un plugin que se aplica al principio, para eliminar el máximo posible de CSS no utilizado y agilizar el trabajo con él:

export default {
  "plugins": {
    "postcss-purgecss": {
      content: ["src/**/*.html"],
      css: ["src/**/*.css"]
    }
  }
}

En la variable css podemos incluir las rutas de nuestro framework CSS. PurgeCSS es muy utilizado con frameworks como TailwindCSS, por ejemplo. Observa, que al contrario de muchos plugins que hemos visto, purgecss no sólo se establece un true para activarlo, sino que hay que indicar varios parámetros de configuración:

Parámetro Descripción
content Indica las rutas donde tenemos contenido HTML o similar: .jsx, .js, .vue, etc...
css Indica las rutas donde tenemos nuestro contenido CSS a revisar.
defaultExtractor Permite definir un extractor de CSS personalizado.
extractors Permite definir un array de extractores de CSS personalizados.
fontFace Elimina reglas @font-face no utilizadas. Por defecto, false.
keyframes Elimina reglas @keyframes no utilizadas. Por defecto, false.
variables Elimina custom properties no utilizadas. Por defecto, false.
rejected Analiza el código eliminado para detectar errores. Por defecto, false.
whitelist Indica selectores CSS que no deben ser eliminados.

Además de whitelist, también tenemos whitelistPatterns y whitelistPatternsChildren, que son de que permiten aprovechar la potencia de las expresiones regulares para crear whitelists más flexibles.

Eliminando CSS no utilizado

Una vez lo tengamos todo instalado y configurado, vamos a hacer una prueba para comprobar si funciona correctamente. Creamos un archivo src/css/index.css (que realmente es PostCSS):

.item {
  background: grey;
}

.warning {
  background: red;
}

.notused {
  background: black;
  width: 200px;
  height: 300px;
}

En este archivo, existen tres clases: .item, .warning y .notused. Las dos primeras existirán en nuestro proyecto, pero la última no. Vamos a comprobar si purgecss elimina ese código muerto del proyecto. Creamos el fichero src/index.html:

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>PostCSS example</title>
    <link rel="stylesheet" href="css/index.css" />
  </head>
  <body>
    <div class="container">
      <div class="item">Item #1</div>
      <div class="warning">Warning!</div>
    </div>
  </body>
</html>

Recuerda que es muy importante asegurarse de tener las opciones content y css indicando todas las rutas con los ficheros implicados en el fichero postcss.config.mjs.

Ahora, solo tendríamos que ejecutar PostCSS para ponerlo a funcionar todo:

$ npx postcss src/css/index.css --no-map

Si todo ha ido bien, esto nos devolverá el siguiente código CSS:

.item {
  background: grey;
}

.warning {
  background: red;
}

Como vemos, ha suprimido la clase .notused porque no ha encontrado ninguna referencia en el código HTML de content. Recuerda que si trabajas con framework o librerías como Vue o React, debes también incluir los archivos .jsx o .vue, ya que pueden incluir clases en ellos.

Eliminar CSS sólo en producción

El proceso de eliminar el CSS no utilizado se realiza cada vez que creamos una nueva versión de nuestro proyecto, y cuando tenemos un servidor de desarrollo, esto suele ocurrir cada vez que modificamos nuestro código. Si nuestro código CSS es muy extenso, es muy posible que este proceso de eliminación de CSS no utilizado sea lento, por lo que puede ser molesto que se realice frecuentemente.

En principio, en desarrollo, eliminar el CSS no utilizado no nos aporta demasiado por lo que podríamos hacer algo para eliminarlo sólo cuando generemos la versión de producción. Esto es muy sencillo si estamos utilizando el fichero de configuración postcss.config.mjs:

const purgecssOptions = {
  content: ["src/**/*.html"],
  css: ["src/**/*.css"]
}

export default {
  "plugins": {
    "postcss-purgecss": process.env.NODE_ENV === "production" ? purgecssOptions : false
  }
}

Observa que lo que hemos hecho es extraer las opciones de purgecss a un objeto purgecssOptions. En su lugar, utilizamos un operador ternario, que comprueba el valor de process.env.NODE_ENV que es una variable de entorno de Node que indica si estamos en modo de desarrollo o en modo de producción.

La variable de entorno NODE_ENV es un convenio que se suele utilizar, donde se suele tener valores como production o prod cuando estamos realizando tareas de producción, y development o dev cuando estamos realizando tareas de desarrollo. No obstante, esto depende de nosotros y de como configuremos estas fases.

¿Quién soy yo?

Soy Manz, vivo en Tenerife (España) y soy streamer partner en Twitch y profesor. Me apasiona el universo de la programación web, el diseño y desarrollo web y la tecnología en general. Aunque soy full-stack, mi pasión es el front-end, la terminal y crear cosas divertidas y locas.

Puedes encontrar más sobre mi en Manz.dev