Las capas de Cascade llegan a tu navegador

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.

Imagen de BEM de una tarjeta con clases
Un ejemplo ilustrado de la denominación BEM de keepinguptodate.com

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.

Ilustración de la demostración del proyecto de desglose de la IU

@layer en acción

Demostración en la que se muestran los colores de los vínculos con las importaciones
Consulta la demostración en CodePen.

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.

Captura de pantalla del proyecto Codepen
Consulta la demostración en CodePen.

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.

Captura de pantalla del proyecto Codepen
Explora el proyecto en Codepen.

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:

Ilustración de Cascade

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: