Saltar al contenido principal

Llegó el CSS Nesting nativo

· 9 min de lectura
Oscar Adrian Ortiz Bustos

Introducción

El CSS Nesting llegó ya, y llegó bailando cha-cha-cha, es una nueva forma de escribir CSS que nos permite anidar selectores dentro de otros. Esta nueva forma de escribir CSS nos permite escribir código más limpio y legible. Hasta hace poco esta funcionalidad solamente se podía utilizar con preprocesadores como Sass o Less, pero ahora ya es posible utilizarla directamente en CSS. Con esta nueva funcionalidad ya no es necesario utilizar selectores largos y repetitivos para modificar estilos de elementos anidados.

En este artículo, exploraremos qué es el CSS Nesting, como funciona y cómo podemos aprovecharlo para crear hojas de estilo más eficientes. También veremos ejemplos prácticos y situaciones donde el CSS Nesting puede marcar una gran diferencia en la organización y legibilidad de nuestro código. Nos daremos cuenta si es necesario seguir usando preprocesadores como Sass o Less, o si ya podemos dejarlos de lado y empezar a utilizar CSS Nesting.

¿Es totalmente compatible?

La respuesta rápida es no, pero la respuesta larga es sí, y te voy a dar la larga. Como mencioné en la introducción está característica únicamente se podía encontrar el los preprocesadores con Sass o Less. Ahora ya llegó al navegador de forma nativa y se espera que en próximas actualizaciones de los diferentes navegadores se vaya implementando de forma nativa.

Como es costumbre cuando se trata de experimentar o probar nuevas funcionalidades en algún navegador siempre se libera primero en Chrome, pero aquí no usamos ese devorador de RAM, así que vamos a ver como podemos activar esta característica en Firefox.

Según lo que nos cuenta la página de Can I Use esta característica está disponible en Firefox y Chrome. Si quieres ver el estado de compatibilidad con otros navegadores puedes visitar la página de Can I Use.

CssNestingToday

Advertencia

La compatibilidad que se muestra en la imagen de arriba corresponde a la fecha de publicación de este artículo, si quieres ver la compatibilidad actualizada puedes visitar la página de Can I Use.

Como podemos ver en la imagen, la compatibilidad con Chrome tiene varias versiones de ventaja sobre los demás navegadores, y aunque en Firefox está disponible desde a partir de la versión 117-119 en versiones anteriores se puede activar de forma experimental.

Activando el CSS Nesting en Firefox

Para realizar esto es necesario ingresar a la página de about:config y activar la bandera layout.css.nesting.enabled y listo, ya podemos utilizar el CSS Nesting en Firefox.

Al ingresar a la página de about:config nos aparecerá este mensaje de advertencia. Si estás de acuerdo con los riesgos que implica activar esta característica experimental puedes hacer clic en el botón Aceptar el riesgo y continuar.

AboutConfigWarning

Una vez dentro de la página de configuración de Firefox buscamos la bandera layout.css.nesting.enabled y la activamos haciendo clic en el botón Activar.

AboutConfigNesting

Nota

La activación de esta característica experimental en Firefox únicamente es compatible con las versiones 115 y 116.

Sintaxis

Hasta antes de Abril del 2023 era necesario colocar las rutas completas de los selectores para poder modificar estilos de elementos anidados. Supongamos que tenemos una estructura HTML con botones y queremos aplicar diferentes estilos a los botones de diferentes tamaños. Para esto tendríamos que escribir algo como esto:

<div class="boton-grande"
<button class="boton">Botonsote</button>
</div>

<div class="boton-pequeno">
<button class="boton">Botoncito</button>
</div>

Para poder aplicar estilos a los botones usando CSS tendríamos que escribir algo como esto:

.boton-grande .boton {
font-size: 1.5rem;
background-color: #000;
}

.boton-pequeno .boton {
font-size: 0.75rem;
background-color: #fff;
}

Con la llegada del CSS Nesting podemos escribir el mismo código de esta forma:

.boton-grande {
.boton {
font-size: 1.5rem;
background-color: #000;
}
}

.boton-pequeno {
.boton {
font-size: 0.75rem;
background-color: #fff;
}
}

¿No es mucho más legible y limpio el código con CSS Nesting? Como podemos ver en el ejemplo anterior, el código es mucho más legible y limpio, y también es mucho más fácil de mantener. Con CSS Nesting podemos escribir código CSS más eficiente y legible, y también podemos escribir código CSS más rápido.

Se pueden anidar o nestear (viva el anglicismo[no le muestren esto a un profesor de inglés o se nos muere]) tantos selectores como queramos, aunque se debe ir con cuidado al ir más allá de 3 niveles de anidación, ya que esto puede hacer que nuestro código sea más difícil de mantener e iría totalmente en contra de la filosofía de CSS Nesting.

Reglas de anidación

Aunque soy de la filosofía de que las reglas están para romperse(a menos que las haya hecho yo), en este caso no es así. Hay algunas reglas que debemos seguir para poder utilizar el CSS Nesting de forma correcta.

Podemos anidar cualquier selector dentro de otro, pero se debe comenzar con un símbolo como & o . para un selector de clase, # para un selector de id, @ para un media query o : para un selector de pseudo-clase.

.elementoPadre {
/* Esto no es válido 😅 */
p {
/* Estilos */
}
}

Para poder arreglar el ejemplo anterior, debemos colocar el símbolo & antes del selector p para poder anidar el selector de forma correcta.

.elementoPadre {
/* Esto si es válido 😉 */
& p {
/* Estilos */
}
}

De forma alternativa también podrías utilizar alguno de estos:

  • > p -- Esto daría estilo a los hijos directos de .elementoPadre
  • :is(p) -- :is() usa la especificidad de los selectores para aplicar estilos
  • :where(p) -- :where() es similar a :is() pero no aumenta la especificidad de los selectores

Todos funcionarían en el ejemplo anterior, pero el más recomendable es utilizar el símbolo & para anidar selectores. El & también nos permite apuntar a pseudo-elementos. Por ejemplo:

.elementoPadre {
&::after {
/* Estilos */
}
&:hover {
/* Estilos */
}
&:target {
/* Estilos */
}
Advertencia

Si no se usa &, apuntará a todos los elementos secundarios del selector y no p.mi-elemento como se esperaba.

Es importante tener en cuenta que podemos utilizar el & en cualquier parte del selector, no necesariamente al inicio. Por ejemplo:

.elementoPadre {
p& {
/* Estilos */
}
}

Esto sería equivalente a una sintaxis no anidada de .elementoPadre p.

Incluso si nos ponemos juguetones podemos usar múltiples & en un selector. Por ejemplo:

ul {
& li & {
/* Estilos */
}
}

Esto apuntaría a los elementos <ul> anidados (ul li ul), pero esto no lo recomiendo si valoras tu cordura.

Finalmente, podemos anidar selectores de media queries. El siguiente código aplica un color cyan a los elementos del párrafo, a menos que el ancho del navegador sea al menos 800px, en cuyo caso el color se cambia a purple.

p {
color: cyan;

@media (min-width: 800px) {
color: purple;
}
}

Errores comunes

El CSS Nesting envuelve los selectores principales en :is(), y esto puede generar algunos errores comunes. Por ejemplo, si tenemos el siguiente código:

.elementoPadre, #elementoPadre2 {}
.elementoHijo {}
}

En el navegador se "traduce" a esto:

:is(.elementoPadre, #elementoPadre2) .elementoHijo {}

Un elemento .elementoHijo dentro de .elementoPadre tiene una especificidad de 101, porque :is() utiliza la especificidad de su selector más específico que en este caso es el ID #elementoPadre2

Nota

Si quieres conocer más sobre la especificidad puedes visitar este link

En Saas el mismo código se compilaría de la siguiente manera:

.elementoPadre .elementoHijo,
#elementoPadre2 .elementoHijo {
/* Estilos */
}

En este caso, un elemento .elementoHijo dentro de .elementoPadre tiene una especificidad de 002, porque coincide con las dos clases (#elementoPadre2 se ignora). Su selector es menos específico que la opción nativa y tiene una mayor probabilidad de ser anulado en la cascada.

También puedes encontrar un problema más sutil, como por ejemplo:

.elementoPadre .elementoHijo {
.elementoAbuelo & {}
}

El equivalente nativo en CSS es:

.elementoAbuelo:is(.elementoPadre .elementoHijo {}

Esto coincide con los siguientes elementos HTML desordenados:

<div class="elementoPadre">
<div class="elementoAbuelo">
<div class="elementoHijo">Esto coincide</div>
</div>
</div>

Al texto Esto coincide se le agregan los estilos porque el analizador de CSS hace esto:

  1. Encuentra todos los elementos con una clase de .elementoHijo que también tienen un ancestro de elementoPadre. Se aplica en cualquier punto de la jerarquía del DOM.
  2. Habiendo encontrado el elemento que contiene el texto "Esto coincide", el analizador verifica si tiene un ancestro de .elementoAbuelo. _De nuevo, en cualquier punto de la jerarquía del DOM. Lo encuentra y estiliza el elemento en consecuencia.

Eso no ocurre en Saas, que se compila a esto:

.elementoAbuelo .elementoPadre .elementoHijo {}

Al HTML anterior no se le agrega ningún estilo, porque las clases de los elementos no sigue estrictamente el orden .elementoAbuelo,.elementoPadre y elementoHijo

Aunado a esto Saas utiliza reemplazo de cadenas, por lo que declaraciones como la siguiente son válidas y coinciden con cualquier elemento con una clase outer-space:

.outer {
&-space { color: black; }
}

El CSS Nativo ignora el selector &-space.

¿Es necesario un preprocesador de CSS?

El uso de preprocesadores de CSS ha sido una práctica común durante mucho tiempo para mejorar la eficiencia y mantener un código más organizado. Sin embargo, con las mejoras en los navegadores y las nuevas capacidades del CSS nativo, ya no parece ser necesario un preprocesador.

Es importante tener en cuenta que los preprocesadores de CSS todavía ofrecen otras ventajas, como la capacidad de agrupar parciales en un solo archivo y reducir el tamaño del código mediante la minificación. Por lo tanto, en proyectos más grandes y complejos, donde se aprovechan al máximo estas funcionalidades, los preprocesadores seguirán siendo valiosos.

Conclusión

La anidación de CSS es una de las características más útiles y prácticas de los preprocesadores. Los fabricantes de navegadores se esforzaron por crear una versión nativa de CSS que fuera lo suficientemente similar para satisfacer a los desarrolladores web. Sin embargo, existen diferencias sutiles y es posible que te encuentres son problemas de especificidad inusuales en selectores (excesivamente) complejos.

La anidación nativa podría hacerte reconsiderar la necesidad de un preprocesador de CSS, pero estos siguen ofreciendo otros beneficios. Saas y herramientas similares van a seguir siendo importantes y esenciales dentro de los kits de herramientas de la mayoría de los desarrolladores. En lo personal me quedo con el CSS nativo, ya que en este punto lo único que puede hacer es mejorar.