CSS @layer: domando la cascada antes de la guerra de selectores
Aprende a usar @layer para diseñar la jerarquía de tu CSS, controlar la precedencia entre estilos y evitar guerras innecesarias de especificidad.
La herramienta que te permite diseñar la jerarquía de tus estilos antes de que la especificidad tome el control. Hoy en Tu Código Cotidiano veremos cómo organizar tu arquitectura.
Hablemos con sinceridad: tu CSS no se rompe solo por especificidad; se rompe porque no tiene jerarquía arquitectónica. Todos hemos lidiado con resets que pisan cosas raras, componentes de UI difíciles de sobreescribir o utilidades que a veces ganan y a veces no. Cuando el CSS se carga desde distintos archivos sin un control fino, la cascada se convierte en una batalla de fuerza bruta donde el selector más pesado es el único que sobrevive.
Ahí es donde @layer cambia las reglas del juego. Esta directiva te permite decidir el orden de precedencia entre grupos de estilos desde el inicio. Eso hace que la cascada deje de ser una pelea improvisada y pase a ser una estructura diseñada. Como lo define MDN, @layer es una regla para declarar capas de cascada y definir su orden de precedencia. La clave absoluta es esta: la precedencia por origen y capa se resuelve antes que la especificidad.
La especificidad ya no lidera la arquitectura; ahora obedece a la capa.
@layer cambia la pelea: la capa decide antes que el selector
Es crucial entender la mecánica interna: antes de que dos selectores entren a competir por el peso de su especificidad, la cascada ya resolvió qué capa tiene la prioridad. La introducción oficial a la cascada de MDN lo establece de forma explícita: si una declaración proviene de una capa con mayor precedencia, su valor se aplicará sin importar si otra declaración en una capa inferior tiene un selector monstruosamente específico.
Esto nos lleva a desmontar uno de los malentendidos más comunes. @layer no "elimina" ni apaga la especificidad; simplemente la encapsula. El peso de un ID o el encadenamiento de cinco clases sigue importando, pero solo dentro de su propia capa. En la arquitectura moderna, primero gana la capa y, únicamente en caso de empate (o si ambas reglas están en el mismo nivel), los selectores sacan sus armas para pelear.
🧠 El nuevo modelo mental de resolución CSS:
- Origen e Importancia: ¿Viene del navegador, del usuario o del desarrollador?
- Capa (Layer): Si el origen es el mismo, gana la capa declarada con mayor precedencia.
- Especificidad: Solo si hay empate de capa, gana el selector más pesado (IDs, clases, etiquetas).
- Orden de aparición: Si todo lo anterior empata, gana la regla escrita más abajo en el archivo.
⚠️ La trampa del !important:
La lógica de precedencia por capas es increíble, pero tiene un giro dramático. Si usas declaraciones !important, el orden de precedencia entre capas se invierte. Una regla !important en una capa inferior ganará sobre una regla !important en una capa superior. No construyas tu modelo mental ignorando este detalle.
Diseñar el orden desde la primera línea
Al entender que la capa manda, la consecuencia lógica es que no podemos improvisar el orden. Conviene que la primera línea de tu archivo CSS maestro sea una declaración explícita de todas tus capas. MDN recomienda fuertemente esta práctica porque te otorga control total sobre la arquitectura desde el arranque. Un "stack" o pila estándar, altamente mantenible, se vería exactamente así:
@layer reset, base, layout, components, utilities, overrides;
Aquí reside una trampa crucial del sistema: el orden de precedencia de las capas se establece en el exacto momento en que se declaran o crean por primera vez, y no se puede cambiar después. Si omites esta declaración inicial y vas creando capas sobre la marcha dispersas en distintos archivos, el navegador asignará la prioridad basándose en qué archivo cargó primero, devolviéndote al mismo caos que intentabas evitar. Diseña la pila primero, escribe código después.
Cómo se crean capas en CSS
Para llevar esta arquitectura a la realidad, CSS nos ofrece tres vías distintas. MDN describe exactamente estas tres formas de interactuar con la directiva:
- Como declaración (statement): Para definir el orden de precedencia global desde el inicio, sin asignar estilos aún.
- Como bloque (bloque @layer): Para inyectar tus reglas CSS reales dentro de una capa específica.
- Con importación (@import): Utilizando la función
layer()para encapsular hojas de estilo externas enteras en el sistema.
/* 1. Declarar el orden (Statement) */
@layer base, componentes;
/* 2. Asignar reglas a una capa nombrada (Bloque) */
@layer componentes {
.btn-primario {
background: #0ea5e9;
color: white;
}
}
/* 3. Importar un archivo externo directamente a una capa */
@import url('reset.css') layer(base);
Un detalle fundamental en esta sintaxis es la diferencia entre capas nombradas y anónimas. Si abres un bloque @layer { ... } sin especificar un identificador, el navegador creará una capa anónima nueva en ese punto exacto del orden. Estas capas son válidas y participan en la cascada, pero tienen una limitación estricta: al no tener nombre, te resulta imposible referenciarlas más adelante en tu archivo para seguir agregándoles reglas.
Sirven para aislar código de un solo uso que no quieres que contamine el resto de tu CSS, pero rompen la escalabilidad si intentas construir un sistema modular donde varios archivos alimentan la misma capa.
/* Capa anónima: no podrás inyectarle más reglas desde otro lugar */
@layer {
.aislado-por-defecto {
margin: 0;
}
}
La trampa de los estilos fuera de capa
Llegamos a uno de los errores más peligrosos al migrar a esta arquitectura. ¿Qué pasa con los estilos normales que no metemos dentro de ninguna capa? La intuición nos diría que, al no ser parte del sistema, pierden prioridad. Sin embargo, ocurre exactamente lo contrario: caen juntos en una capa implícita final. Como explica MDN, los estilos unlayered (sin capa) forman una capa final que siempre queda al final del orden de precedencia y sobreescribe al resto.
Traduzcamos esto a una consecuencia práctica: si metes casi todo tu CSS en capas meticulosamente diseñadas, pero dejas una o dos reglas sueltas fuera (por ejemplo, en un archivo antiguo que olvidaste migrar), esas reglas externas terminarán ganando de forma sorpresiva y romperán tus componentes. Esta es la regla de oro: si adoptas @layer, mételo todo en capas. Dejar estilos "huérfanos" es invitar al caos.
Un caso real: componentes, utilidades y overrides
Llevemos esta arquitectura a un código de producción. Imagina que tienes un componente de tarjeta con un botón, pero necesitas aplicar una clase utilitaria para ocultarlo. Históricamente, si el selector del componente (.card .btn) era más específico que la clase de utilidad (.hidden), la utilidad fallaba y te veías obligado a usar el temido !important. Mira cómo se resuelve ahora:
Comentarios y valoraciones
No hay comentarios aún. ¡Sé el primero en opinar!