¿Qué es el alcance en CSS?

Estrategias para aislar CSS y evitar conflictos


Una de las características más deseadas en CSS por los desarrolladores siempre ha sido poder aplicar estilos de forma local. En programación, estamos acostumbrados a evitar trabajar de forma global, y delimitar al máximo el código que realizamos, de forma que no tenga efectos secundarios en el resto de nuestro programa o aplicación. Por lo tanto, puede parecer lógico que en CSS deseemos hacer lo mismo.

El caso habitual

El caso más claro para ilustrar esto, es aquel donde estamos creando estilos CSS en una parte concreta de nuestra página. Todo va maravillosamente bien y funciona correctamente.

Tenemos el siguiente fragmento de HTML. Nuestra idea es darle estilo al elemento .element:

<div class="parent">
  <div class="element">Element</div>
</div>

<style>
  .element {
    background: indigo;
    color: white;
    padding: 1rem;
  }
</style>

En principio, asumimos que no vamos a tener más etiquetas .element en el resto de nuestro sitio (algo que no tiene porque ser así), por lo que no vemos mayor problema y continuamos escribiendo código.

Todo va bien y resolvemos la tarea concreta. El problema (que vendrá después) ocurre porque en este punto no solemos tener una visión global de lo que estamos haciendo, ya que estamos concentrados en resolver un problema local y concreto. Y una vez resuelto, nos olvidamos de él.

El problema

A medida que pasa el tiempo, y el código y proyecto crece, nos aparecen necesidades de añadir nuevos elementos .element (para reutilizar cierta parte de lo que ya tenemos escrito), pero también tienen características diferentes al anterior.

Como tradicionalmente, la naturaleza de CSS siempre ha sido global, cualquier cambio que hagamos repercute en todo el documento (salvo que lo tengamos bien acotado). Esto requiere separar los estilos en dos partes, tarea que suele ser compleja y requiere tiempo. Por lo que, intentando evitar gastar mucho tiempo, un primer impulso para solucionar esto nos podría llevar a añadir un padre para delimitarlo:

<div class="parent">
  <div class="element">Element</div>
</div>

<style>
  .parent .element {
    background: indigo;
    color: white;
    padding: 1rem;
  }
</style>

Realmente, esto no resuelve el problema (bueno, sí, pero lo más seguro es que sólo provisionalmente). Lo único que estamos haciendo es aumentar el problema a un tercer nivel y posponerlo a un posible futuro. Y de paso, aumentamos la especificidad del selector, lo que puede dar más problemas derivados.

Soluciones

Estos problemas son muy difíciles de predecir, y debido a la mencionada naturaleza global de CSS también son muy difíciles de solucionar, por lo que la industria ha buscado y creado multitud de sistemas para solucionarlo, cada uno con sus ventajas y desventajas:

Solución¿Problema?CómodoAconsejable
Formas nativas (sólo CSS)
Forzar estilos usando !importantCasi siempre la peor solución.
Reorganizar los selectores y hacerlos más específicosSube la especificidad CSS.🤷‍♀️
Reescribir los selectores para que no colisionenCostoso y dificil evitar conflictos.
Usar la regla @scope para delimitar el alcance de estilosCómodo, nativo y fácil de usar.
Formas nativas (con Javascript)
Usar Shadow DOM para encapsular DOM y evitar colisionesEfectivo, pero complejo.
Librerías/herramientas no nativas
Usar nomenclaturas/metodologías como BEMEvita conflictos, pero verboso.🤷‍♀️
Herramientas de renombrado de clases (evitar colisiones)Cómodo, requiere build-step.
Usar Tailwind (u otras) para evitar pensar en estas cosasCómodo, genera otros problemas.

¿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