// css · Web Platform Advent #19
Capas de cascada CSS: cómo @layer hace negociable la especificidad
Las capas de cascada permiten decidir qué reglas ganan ordenando capas en lugar de escalar selectores. Cómo funciona @layer y los dos puntos donde el orden de prioridad se invierte.
La mayoría de los problemas de especificidad en CSS no son realmente problemas de especificidad. Son dos hojas de estilo en desacuerdo, y la única herramienta disponible es hacer un selector más pesado que el otro. Las capas de cascada sustituyen esa carrera armamentística por un orden explícito.
Qué cambia realmente una capa
MDN expone el beneficio sin rodeos: las reglas dentro de una capa de cascada se aplican en cascada juntas, lo que da a quienes desarrollan para la web más control sobre la cascada. La consecuencia es la parte interesante, porque elimina una restricción que probablemente llevas años sorteando.
Puedes usar selectores más simples, porque ya no tienes que asegurarte de que un selector tenga la especificidad suficiente para anular a una regla competidora. Basta con asegurarte de que aparezca en una capa posterior.
El mecanismo es contundente: una vez establecido el orden de las capas, la especificidad y el orden de aparición se ignoran entre capas. Una regla de una capa posterior se aplica aunque tenga menos especificidad que la regla a la que anula. Dentro de una misma capa, las reglas normales de especificidad siguen decidiendo entre reglas competidoras, así que nada de lo que sabes se pierde: simplemente deja de aplicarse a través de las fronteras entre capas.
Declarar el orden por adelantado
El orden lo fija el orden de declaración: la primera capa declarada recibe la prioridad más baja, y la última capa declarada la más alta.
Eso convierte la forma de sentencia en la más útil a la que recurrir, porque fija todo el orden en una sola línea antes de que exista ninguna regla:
@layer theme, layout, utilities; A partir de ahí, cualquier cosa en utilities gana a cualquier cosa en layout, que gana a cualquier cosa en theme, sin importar cómo estén escritos los selectores.
Un detalle hace que esto sea robusto en una base de código real. Una vez declarados los nombres, se añaden reglas a una capa volviendo a declarar su nombre, y los estilos se anexan a esa capa sin alterar el orden. Así puedes repartir una capa entre muchos archivos, importados en cualquier secuencia, y la prioridad que decidiste en esa primera línea sigue vigente.
La primera inversión: los estilos sin capa ganan
Aquí es donde la intuición falla a casi todo el mundo, y merece la pena enunciarlo en los términos más rotundos que usa MDN.
Los estilos que no están definidos en una capa siempre anulan a los estilos declarados en capas con nombre y anónimas.
Léelo frente a la suposición natural. Esperarías que las capas fueran una jerarquía a la que el CSS corriente se incorpora por abajo. Es justo lo contrario: el CSS sin capa se sitúa por encima de todas las capas que declaraste, en el caso de las declaraciones normales.
Esto no es un accidente, y resulta genuinamente útil en cuanto le ves la forma. Mete una hoja de estilo de terceros o un reset en una capa y tus propias reglas sin capa la anulan sin una sola pelea de especificidad. La trampa solo existe para quien pone todo en capas salvo un archivo olvidado, y luego no consigue entender por qué ese archivo siempre gana.
La segunda inversión: !important le da la vuelta a todo
La otra sorpresa es que el orden de prioridad entre reglas importantes es el inverso del de las reglas normales.
MDN detalla ambas mitades. Dentro de los estilos de autor, todas las declaraciones importantes situadas dentro de capas CSS tienen precedencia sobre las declaraciones importantes situadas fuera de una capa. Y todas las declaraciones normales dentro de capas tienen menos prioridad que las declaraciones fuera de una capa.
Así que una misma base de código tiene dos órdenes corriendo en direcciones opuestas a la vez. Para las declaraciones normales, gana la última capa y el CSS sin capa gana a todo. Para las declaraciones !important, gana la primera capa declarada, y los estilos en capas ganan a los que no están en ninguna.
En la práctica, esto significa que un reset colocado en tu primera capa puede hacer que una declaración sea realmente inanulable marcándola como importante, lo cual es una capacidad real y también un arma de doble filo real. También significa que depurar un problema de cascada exige saber si la declaración implicada es importante antes siquiera de razonar sobre el orden de las capas.
Cómo usarlo sin sorpresas
Tres hábitos cubren casi todo.
Declara el orden completo en una línea, pronto. Una sola sentencia @layer al principio de tu hoja de estilo de entrada es toda la configuración. Quien la lea sabe qué gana a qué sin abrir otro archivo.
Decide deliberadamente qué se queda fuera de las capas. Dado que el CSS sin capa está por encima de todas las capas en las declaraciones normales, dejar estilos fuera de una capa es una decisión con consecuencias, no un valor por defecto neutro. O metes todo en una capa, o mantienes el conjunto sin capa pequeño e intencionado.
Trata las declaraciones importantes como un sistema aparte. No siguen el orden que diseñaste, siguen su imagen especular. Si te descubres recurriendo a !important dentro de una arquitectura en capas, lo que normalmente había que ajustar era el orden de las capas.
Las capas de cascada no hacen que CSS sea más fácil de aprender, añaden un mecanismo con dos inversiones contraintuitivas. Lo que sí hacen es convertir el desenlace de un conflicto en algo que decidiste de antemano en lugar de algo que descubriste contando selectores.