Las capas de Cascade (la regla de CSS @layer
) llegarán a Chromium 99, Firefox 97 y Safari 15.4 Beta. Permiten un control más explícito de tus archivos CSS para evitar conflictos de especificidad de estilo. Esto es particularmente útil para bases de código grandes, sistemas de diseño y cuando se administran estilos de terceros en aplicaciones.
Colocar capas de tu CSS de forma clara evita anulaciones de estilo inesperadas y promueve una mejor arquitectura de CSS.
La especificidad de CSS y la cascada
La especificidad de CSS es la forma en la que CSS decide qué estilos aplicar a cuáles elementos. Los distintos selectores que puedes utilizar determinan la especificidad de cualquier regla de diseño. Por ejemplo, los elementos son menos específicos que las clases o los atributos, que a su vez son menos específicos que los ID. Esta es una parte fundamental del aprendizaje de CSS.
Las personas recurren a las convenciones de nomenclatura de CSS, como BEM, para evitar anular la especificidad de forma involuntaria. Si le das a todo un solo nombre de clase, todo se colocará en el mismo plano de especificidad. Sin embargo, no siempre es posible mantener estos estilos organizados, especialmente cuando se trabaja con código de terceros y sistemas de diseño.
El objetivo de las capas de Cascade es resolver este problema. Agregan una capa nueva a la cascada de CSS. Con los estilos en capas, la prioridad de una capa siempre supera la especificidad de un selector.
Por ejemplo, el selector .post a.link
tiene una especificidad más alta que .card a
. Si intentas aplicar un estilo a un vínculo, en una tarjeta, verás que se aplicará el selector más específico.
Si usas @layer
, puedes ser más explícito sobre la especificidad del estilo de cada uno y asegurarte de que los estilos del vínculo de tu tarjeta anulen los del vínculo de la publicación, aunque la especificidad podría ser numéricamente menor si todo el CSS estuviera en el mismo plano. Esto se debe a la precedencia en cascada. Los estilos en capas crean nuevos "planos" en cascada.
@layer
en acción
En este ejemplo, se muestra la potencia de las capas de cascada mediante @layer
. Se muestran varios vínculos: algunos sin ningún nombre de clase adicional aplicado, uno con una clase .link
y otro con una clase .pink
. Luego, el CSS agrega tres capas: base
, typography
y utilities
, de la siguiente manera:
@layer base {
a {
font-weight: 800;
color: red; /* ignored */
}
.link {
color: blue; /* ignored */
}
}
@layer typography {
a {
color: green; /* styles *all* links */
}
}
@layer utilities {
.pink {
color: hotpink; /* styles *all* .pink's */
}
}
En definitiva, todos los enlaces son verdes o rosados. Esto se debe a que, si bien .link
tiene una especificidad de nivel de selector mayor que a
, hay un estilo de color en a
en una @layer
de mayor precedencia. a { color: green }
anula .link { color: blue }
cuando la regla verde está en una capa después de la regla azul.
La prioridad de las capas supera la especificidad del elemento.
Organización de las capas
Puedes organizar las capas directamente en la página, como se muestra más arriba, o bien puedes organizarlas en la parte superior de un archivo.
El orden de las capas se establece la primera vez que el nombre de cada capa aparece en el código.
Es decir, si agregas lo siguiente en la parte superior del archivo, todos los vínculos aparecerán en rojo y el vínculo con la clase .link
se mostrará en azul:
@layer utilities, typography, base;
Esto se debe a que el orden de las capas ahora se revierte, lo que coloca las utilidades en primer lugar y las bases al final. Por lo tanto, las reglas de estilo de la capa base
siempre tendrán una especificidad mayor que las de la capa de tipografía. Ya no serán vínculos verdes, sino rojos o azules.
Cómo organizar importaciones
Otra forma de usar @layer
es con archivos de importación. Puedes hacerlo directamente cuando importes estilos con una función layer()
, como se muestra en el siguiente ejemplo:
/* Base */
@import '../styles/base/normalize.css' layer(base); /* normalize or rest file */
@import '../styles/base/base.css' layer(base); /* body and base styles */
@import '../styles/base/theme.css' layer(theme); /* theme variables */
@import '../styles/base/typography.css' layer(theme); /* theme typography */
@import '../styles/base/utilities.css' layer(utilities); /* base utilities */
/* Layouts */
@import '../styles/components/post.css' layer(layouts); /* post layout */
/* Components */
@import '../styles/components/cards.css' layer(components); /* imports card */
@import '../styles/components/footer.css' layer(components); /* footer component */
El fragmento de código anterior tiene tres capas: base
, layouts
y components
. Los archivos de normalización, tema y tipografía en base
, con un archivo post
en layouts
, y cards
y footer
en components
. Cuando se importa el archivo, se crean instancias de las capas con la función Layer. Un enfoque alternativo sería organizar las capas en la parte superior del archivo y declararlas antes de realizar cualquier importación:
@layer base,
theme,
layouts,
components,
utilities;
Ahora, el orden en el que @import
tus estilos no le importará al orden de la capa, dado que ya está establecido en la primera instancia del nombre de la capa. Una cosa menos de la que debe preocuparse. Aún puedes configurar los archivos importados en capas específicas, pero el orden ya está establecido.
Capas y cascada
Demos un paso atrás y veamos dónde se utilizan las capas en relación con la cascada más amplia:
El orden de precedencia es el siguiente:
- Usuario-agente normal (prioridad más baja)
- Usuario local @layer
- Usuario local normal
- Autor: @layers
- Autor normal
- Autor: !important
- Autor: @layer !important
- El usuario local es importante.
- Usuario-agente !important** (prioridad más alta)
Como puedes ver aquí, los estilos de @layer !important
se invierten. En lugar de ser menos específicos que los estilos sin capas (normales), tienen mayor precedencia. Esto se debe a cómo funciona !important
en la cascada: interrumpe la cascada normal en tus hojas de estilo y revierte la especificidad normal del nivel de capa (prioridad).
Capas anidadas
Las capas también pueden anidarse dentro de otras capas. El siguiente ejemplo proviene de la explicación de Capas de cascada de Miriam Suzanne:
@layer default {
p { max-width: 70ch; }
}
@layer framework {
@layer default {
p { margin-block: 0.75em; }
}
p { margin-bottom: 1em; }
}
En el fragmento de código anterior, puedes acceder a framework.default
usando un .
como representador de la capa default
que se anida en framework
. También puedes escribir esto en un formato más abreviado:
@layer framework.default {
p { margin-block: 0.75em }
}
Las capas y el orden de las capas resultantes son los siguientes:
- predeterminado
framework.default
framework
sin capas- sin capas
Aspectos que se deben tener en cuenta
Las capas en cascada pueden ser excelentes si las usas correctamente, pero también pueden crear más confusión y resultados inesperados. Ten en cuenta lo siguiente cuando trabajes con capas de cascada:
Regla 1: No uses @layer
para determinar los alcances
Las capas en cascada no resuelven el alcance. Si tienes un archivo CSS con un @layer
, di card.css
y deseas aplicar estilo a todos los vínculos de la tarjeta, no escribas estilos como los siguientes:
a {
…
}
Esto hará que todas las etiquetas a
de tu archivo obtengan esta anulación. Aun así, es importante limitar sus estilos de forma adecuada:
.card a {
…
}
Regla 2: Las capas de cascada se ordenan detrás de CSS sin capas
Es importante tener en cuenta que un archivo CSS en capas no anulará las capas CSS que no tengan capas. Esta fue una decisión intencional para facilitar la introducción de capas de una manera más sensata para trabajar con tu base de código existente. Por ejemplo, usar un archivo reset.css
es un buen punto de partida y un buen caso de uso para las capas en cascada.
Regla 3: !important
invierte la especificidad de la cascada
Si bien los estilos con capas son menos específicos que los estilos sin capas en general, usar !important
revierte esto. En una capa, las declaraciones con la regla !important
son más específicas que los estilos sin capas.
En ese caso, los estilos de !important
invierten su especificidad. En el diagrama anterior, se muestra esto a modo de referencia: autor @layers tiene menos precedencia que autor normal que tiene menos precedencia que author !important que tienen menos precedencia que author @layer !important.
Si tienes varias capas, la primera con !important
tendrá prioridad de !important
y será el estilo más específico.
Regla 4: Comprende los puntos de inserción
Dado que el orden de las capas se establece la primera vez que aparece cada nombre de capa en tu código, si colocas una declaración @layer
después de importar y configurar layer()
, o después de una sentencia @layer
diferente, se puede ignorar. A diferencia de CSS, en el que la regla de estilo que se encuentra más abajo en la página se aplica a las capas de cascada, el orden se establece en primera instancia.
Puede estar en una lista, en un bloque de capas o en una importación. Si colocas @layer
después de una lista de importación con layer()
, no hará nada. Si lo colocas en la parte superior del archivo, se establecerá el orden de las capas y te ayudará a ver claramente las capas dentro de la arquitectura.
Regla n.o 5: Observa tu especificidad
Con las capas de cascada, un selector menos específico (como a
) anulará uno más específico (como .link
) si ese selector menos específico está en una capa más específica. Ten en cuenta lo siguiente:
a
en layer(components)
anulará .pink
en layer(utilities)
si se especificara @layer utilities, components
. Si bien es una parte intencional de la API, esto puede ser confuso y frustrante si no lo esperas.
Por lo tanto, si escribes clases de utilidad, siempre inclúyelas como una capa de orden superior que los componentes con los que deseas anularlas. Tal vez pienses que acabo de agregar esta clase .pink
para cambiar el color y no se aplica.
Más información sobre las capas de cascada
También puedes consultar estos recursos para obtener más información sobre las capas en cascada: