CSS, como todos sabemos, son las siglas de Cascade Style Sheet, pero ¿conocemos realmente cómo funciona la cascada de CSS?

Generalmente nos centramos en dos conceptos clave para controlarla: el orden de instanciamiento y la especificidad, pero existen más conceptos implicados en ella y en un futuro no muy lejano tendremos alguno nuevo como las “capas en cascada” de las que hablaremos al final de este post.

Por ello vamos a ir desgranando cada uno de ellos a continuación.

¿Qué es la cascada?

La cascada de CSS es la forma en la que el navegador interpreta nuestra hoja de estilos, resolviendo las declaraciones CSS y los posibles conflictos entre ellas.

A veces estamos declarando un nuevo estilo para un elemento y nos topamos con que no se está aplicando correctamente y en ocasiones la frustración nos lleva a querer tirar de nuestro comodín “!important” para resolverlo.

Cada vez que escribimos una regla en CSS, esta entra automáticamente en la cascada y el navegador se ocupa de darle su lugar en ella, de forma que una regla que esté al principio de la cascada tendrá más posibilidades de ser el estilo final que aplique que una que esté al final de la cascada. ¡Cuidado! No debemos confundir el lugar que ocupa una regla en la cascada con el lugar que ocupa esa misma regla dentro de la hoja de estilos. Es probable que estemos acostumbrados a creer que cuanto más abajo esté una regla CSS más sobrescribirá a las que estén por encima y por ello tendrá un lugar alto en la cascada, pero no es así.

Recordad siempre: el lugar que ocupa tu regla CSS en la cascada lo decide el navegador.

¿Cómo funciona la cascada?

Importancia

El primer punto que tiene en cuenta el navegador para gestionar nuestras reglas CSS es el tipo de regla que estamos utilizando. Hay 4 tipos:

  1. transiciones: las reglas que definamos que incluyan transiciones son las de mayor nivel de importancia para el navegador y, por lo tanto, serán las que ocupen un lugar superior en la cascada.
  2. !important: también conocido como “vas a aplicar mis estilos sí o sí”, es nuestro socorrista cuando tenemos que sobrescribir estilos que no dependen de nosotros y nuestra marca de la vergüenza cuando lo usamos para sobrescribirse a nosotros mismos.
  3. animaciones: las reglas que aplican a una animación activa adquieren un nivel por encima de las reglas normales en la cascada.
  4. normales: el resto de reglas.

En base a la importancia de las reglas deducimos que el navegador siempre tendrá en cuenta en primer lugar los estilos para transiciones/animaciones y nuestros intentos desesperados de aplicar estilos usando “!important”.

Origen

Lo segundo que tiene en cuenta el navegador es el origen de los estilos. En este punto tenemos que tener en cuenta que el navegador va a valorar como “originario del sitio” todos los estilos que nosotros estemos aplicando en nuestro desarrollo. Es decir, tanto si tenemos un desarrollo pequeño en el que sólo cargamos una hoja de estilos como si tenemos un desarrollo más grande en el que utilizamos distintas hojas e incluso estilos de terceros, todo ello será considerado como de origen único por el navegador.

El orden en el que el navegador interpretará dónde están definidas las reglas será este:

  1. website: todo lo que tengamos definido en nuestro desarrollo.
  2. user: todo lo que esté definido en la hoja de estilos personalizada que tenga aplicada el usuario.
  3. navegador: los navegadores tienen estilos predefinidos por defecto para muchos elementos (que generalmente reseteamos en nuestros estilos de website).

Hay que tener en cuenta que esta prioridad está combinada con la anterior por lo que una regla que esté definida como “!important” en los estilos de usuario o del navegador, estará por encima de la definida en el website.

Especificidad

La especificidad es el concepto que más manejamos respecto a la cascada, a pesar de que ocupa el tercer lugar en la creación de la cascada por parte del navegador.

El orden de aplicación de esta especificidad es el siguiente:

  1. Estilos en línea: cualquier estilo que esté definido en línea será el que mayor especificidad tenga.
  2. id
  3. class | pseudo-clases | attribute
  4. etiqueta HTML | pseudo-elements

Es importante recordar que el selector universal (*) así como los selectores de combinación (>, +, ||, ~, ‘_’ ) y la pseudo clase de negación (:not()) no tienen ningún tipo de efecto en la especificidad.

Explicado así parece sencillo, pero no lo es. El cálculo de la especificidad de una regla es algo complejo y está combinado con todos los puntos anteriores. Por ello puede llevarnos a errores si la consideramos como un ente individual:

See the Pen QWMEEQX by @NoemiMS on CodePen.

¿Cómo podemos calcular la especificidad de un estilo?

Para calcular la especificidad de una declaración CSS utilizaremos un número de cuatro dígitos, en el que cada uno de los dígitos corresponde a lo siguiente:


#miLista #miItem #enlace { ... } /* En este caso sería 3 */
#miLista {...} /* En este caso sería 1 */


.btn .icon-grey { ... } /* En este caso sería 2 */
.list .item .link {...} /* En este caso sería 3 */

p::after { ... } /* En este caso sería 2 */
p span::before {...} /* En este caso sería 3 */

En el siguiente ejemplo calculamos la especificidad de distintas reglas:

body {color: lime;} /* 0001  */
body div.miClase a {color: limegreen;} /* 0013 */
.miClase {color: red;} /* 0010 */
div.miClase {color: blue;}  /* 0011 */
div.miClase a {color: gold;}  /* 0012 */
div:last-child {color: orange;}  /* 0011 */

Y aquí podemos ver que la segunda regla que tiene una especificidad de 0013 será la que se aplique:

See the Pen wvqJLVr by @NoemiMS on CodePen.

Es importante entender esto, pero no es necesario memorizarlo porque existen calculadoras de especificidad si en algún momento nos surgen dudas.

Posición

Por último, lo que el navegador tendrá en cuenta para aplicar las reglas CSS es la posición que éstas ocupan en la hoja de estilos.

Conclusión

La cascada es el concepto más “abstracto” en CSS y el que más cuesta asimilar, pero como hemos podido ver es bastante sencillo si se entiende y aprende bien desde el principio y no debemos acotarlo únicamente a la especificidad de los selectores, hay que tener una visión global de todo lo que entra en juego en el navegador para aplicar los distintos estilos. De esta manera siempre lograremos tener un control total sobre ellos, nos ahorraremos abusar de los “!importants”.

Extra Ball 🔮 Cascade Layers

A finales de 2019 se propuso añadir un nuevo nivel a la cascada que se denominó “capas de cascada” o Cascade Layers. Actualmente no está 100% estandarizado, pero se pueden hacer pruebas con ello activando su soporte experimental en navegadores (Firefox, Safari y Chrome).

¿Qué son las capas de cascada?

Pues son lo que su propio nombre indica que son. Nos permiten crear distintas capas dentro de nuestro código de manera que podemos organizar y equilibrar las preocupaciones de la cascada dentro de un mismo origen. Si has trabajado alguna vez con Vue, es algo similar a definir estilos scope en tu componente, pero de manera nativa en CSS (recordad que no todo el mundo utiliza frameworks 😉).

¿Cómo funcionan?

Primero se han de definir y para ello tenemos tres formas:

@layer nameLayer { 
/*incluimos aquí los estilos de la capa */
}
@layer nameLayer; 
/* Sólo la estamos definiendo, podemos meter los estilos después */
@import(reset.css) layer(reset); 
/*  podemos crear una capa con estilos de otra hoja */

Las capas pueden o no tener nombre, pero lo ideal sería tenerlas identificadas cada una de ellas para poder tener un mejor mantenimiento de las mismas.

Las capas, una vez se han instanciado por primera vez, adquieren su posición en el orden de aplicación de los mismos, de manera que si definimos la cascada reset, layout y component al inicio de nuestro código, más adelante podremos añadir estilos que automáticamente ocuparan la posición en la que esté definida esa capa.

@layer reset { 
/* estilos reset */
}
@layer layout { 
/* estilos layout */
}
@layer component;
@layer component2 { 
/* estilos componente 2  */
}
....
/* 350 líneas más tarde */
....
@layer component { 
/* estilos de componente que automáticamente se añadirán a la capa en la // posición en la que la definimos anteriormente  * /

La buena práctica que se pretende estandarizar es que se definan, al inicio de los estilos, las capas que se van a utilizar y posteriormente se vayan utilizando para meter los estilos que deban contener.

¿Cómo afecta a la cascada?

Las capas de cascada se colarían entre el origen y la especificidad.

Dentro de las capas, como hemos visto, prevalecerá el propio orden de la cascada, en el ejemplo anterior si tuviéramos un estilo definido en “component” y este se sobrescribe en “component2”, el navegador dará como ganador al estilo del componente 2 aunque en el código el estilo en la capa component esté definido muchas líneas “más abajo” de la cascada.

Pero si en las capas están definidos unos estilos y, posteriormente por especificidad, estos “debieran” cambiar, no lo harán. ¿Por qué? Las capas de cascada tienen una importancia mayor en el orden de determinación de estilos, por lo que el navegador otorgará prioridad a los estilos definidos en las capas antes que a cualquiera fuera de ellos, por mucho que el selector sea más específico.

Esto nos puede ofrecer muchas más posibilidades sin necesidad de prestar tanta atención a la especificidad y simplificar nuestros selectores.

Como he indicado al principio esta especificación aún es experimental, pero no está de más que vayamos haciéndonos una idea de ella. Si queréis aprender más, en este post se explican muy bien y con multitud de ejemplos (y actualizaciones periódicas si hay novedades).

Cuéntanos qué te parece.

Los comentarios serán moderados. Serán visibles si aportan un argumento constructivo. Si no estás de acuerdo con algún punto, por favor, muestra tus opiniones de manera educada.

Suscríbete

Estamos comprometidos.

Tecnología, personas e impacto positivo.