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ómodo | Aconsejable |
---|---|---|---|
Formas nativas (sólo CSS) | |||
Forzar estilos usando !important | Casi siempre la peor solución. | ✅ | ❌ |
Reorganizar los selectores y hacerlos más específicos | Sube la especificidad CSS. | ❌ | 🤷♀️ |
Reescribir los selectores para que no colisionen | Costoso y dificil evitar conflictos. | ❌ | ✅ |
Usar la regla @scope para delimitar el alcance de estilos | Cómodo, nativo y fácil de usar. | ✅ | ✅ |
Formas nativas (con Javascript) | |||
Usar Shadow DOM para encapsular DOM y evitar colisiones | Efectivo, pero complejo. | ❌ | ✅ |
Librerías/herramientas no nativas | |||
Usar nomenclaturas/metodologías como BEM | Evita conflictos, pero verboso. | ❌ | 🤷♀️ |
Herramientas de renombrado de clases (evitar colisiones) | Cómodo, requiere build-step. | ✅ | ❌ |
Usar Tailwind (u otras) para evitar pensar en estas cosas | Cómodo, genera otros problemas. | ✅ | ❌ |