Las capas de Cascade llegan a tu navegador

Las capas en cascada (la regla 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 especialmente útil para bases de código grandes, sistemas de diseño y cuando se administran estilos de terceros en aplicaciones.

Dividir tu CSS en capas de forma clara evita anulaciones de diseño inesperadas y promueve una mejor arquitectura de CSS.

Especificidad del CSS y la cascada

La especificidad de CSS es la forma en que CSS decide qué estilos aplicar a qué elementos. Los diferentes selectores que puedes usar determinan la especificidad de cualquier regla de estilo. Por ejemplo, los elementos son menos específicos que las clases o los atributos, que a su vez son menos específicos que los IDs. Esta es una parte elemental del aprendizaje de CSS.

Las personas recurren a convenciones de nombres de CSS, como BEM, para evitar anular la especificidad de forma no intencional. Si asignas un solo nombre de clase a todo, todo se coloca en el mismo plano de especificidad. Sin embargo, no siempre es posible mantener estilos tan organizados, en especial cuando se trabaja con código y sistemas de diseño de terceros.

Visualización de BEM de una tarjeta con clases
Un ejemplo ilustrado de nombres de BEM de keepinguptodate.com.

El objetivo de las capas en cascada es resolver este problema. Presentan una nueva capa 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 diseño a un vínculo dentro de una tarjeta, dentro de una publicación, verás que se aplicará el selector más específico.

Cuando usas @layer, puedes ser más explícito sobre la especificidad de cada uno y asegurarte de que los estilos del vínculo de la tarjeta anulen los del vínculo de la publicación, aunque la especificidad podría ser numéricamente menor si todo tu CSS estuviera en el mismo plano. Esto se debe a la prioridad en cascada. Los estilos en capas crean nuevos "planos" en cascada.

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

@layer en acción

Demostración que muestra los colores de los vínculos con importaciones
Mira la demostración en Codepen.

En este ejemplo, se muestra la potencia de las capas en cascada con @layer. Se muestran varios vínculos: algunos sin nombres de clase adicionales, uno con una clase .link y uno 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 última instancia, todos los vínculos son verdes o rosas. Esto se debe a que, si bien .link tiene una especificidad a nivel del selector más alta que a, hay un estilo de color en a en un @layer de mayor prioridad. a { color: green } anula .link { color: blue } cuando la regla verde está en una capa después de la regla azul.

La prioridad de la capa supera la especificidad del elemento.

Cómo organizar las capas

Puedes organizar las capas directamente en la página, como se muestra arriba, o bien en la parte superior de un archivo.

El orden de las capas se establece la primera vez que aparece el nombre de cada capa en tu código.

Eso significa que, si agregas lo siguiente a la parte superior del archivo, todos los vínculos aparecerán en rojo y el vínculo con la clase .link aparecerá en azul:

@layer utilities, typography, base;

Esto se debe a que el orden de las capas ahora está invertido, lo que coloca las utilidades primero y la base al final. Por lo tanto, las reglas de estilo en la capa base siempre tendrán una especificidad más alta que las reglas de estilo en la capa de tipografía. Ya no serán vínculos verdes, sino rojos o azules.

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

Organiza las importaciones

Otra forma de usar @layer es con archivos de importación. Puedes hacerlo directamente cuando importas estilos con una función layer(), como 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 crea una instancia de las capas con la función de capa. Un enfoque alternativo sería organizar tus capas en la parte superior del archivo y declararlas antes de cualquier importación:

@layer base,
       theme,
       layouts,
       components,
       utilities;

Ahora, el orden en el que @import tus estilos no importará para el orden de las capas, ya que ya se estableció en la primera instancia del nombre de la capa. Una preocupación menos. Aún puedes establecer archivos importados en capas específicas, pero el orden ya está establecido.

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

Capas y la cascada

Hagamos un paso atrás y veamos dónde se usan las capas en relación con la cascada más amplia:

Ilustración de cascada

El orden de prioridad 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
  • Usuario local !important
  • Usuario-agente !important** (prioridad más alta)

Aquí, es posible que notes que los estilos de @layer !important están invertidos. En lugar de ser menos específicos que los estilos sin capas (normales), tienen una prioridad más alta. Esto se debe a la forma en que !important funciona en la cascada: rompe la cascada normal en tus hojas de estilo y revierte la especificidad normal a nivel de la capa (prioridad).

Capas anidadas

Las capas también se pueden anidar dentro de otras capas. El siguiente ejemplo proviene de la explicación de las capas en 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 indicador de que la capa default está anidada dentro de 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 capas resultantes son los siguientes:

  • predeterminado
  • framework.default
  • framework sin capas
  • sin capas

Aspectos que debes tener en cuenta

Las capas en cascada pueden ser muy útiles si se usan correctamente, pero también pueden generar confusión adicional y resultados inesperados. Ten en cuenta lo siguiente cuando trabajes con capas en cascada:

Regla 1: No uses @layer para definir el alcance

Las capas en cascada no resuelven el alcance. Si tienes un archivo CSS con un @layer, por ejemplo, card.css, y deseas aplicar diseño 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 reciban esta anulación. Aún es importante definir el alcance de tus estilos correctamente:

.card a {
  
}

Regla 2: Las capas en cascada se ordenan detrás del CSS sin capas.

Es importante tener en cuenta que un archivo CSS con capas no anulará el CSS sin 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 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 sin capas en general, usar !important invierte esta situación. En una capa, las declaraciones con la regla !important son más específicas que los estilos sin capas.

En ese caso, los estilos !important invierten su especificidad. El diagrama anterior muestra esto a modo de referencia: author @layers tiene menos prioridad que author normal, que tiene menos prioridad que author !important, que tiene menos prioridad que author @layer !important.

Si tienes varias capas, la primera capa con !important tendrá prioridad sobre !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 el nombre de cada 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 se aplica la regla de estilo más abajo en la página para las capas en cascada, el orden se establece en la 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 se hará nada. Si lo colocas en la parte superior del archivo, se establecerá el orden de las capas y te ayudará a ver con claridad las capas dentro de la arquitectura.

Regla 5: Ten cuidado con la especificidad

Con las capas en cascada, un selector menos específico (como a) anulará un selector 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ía .pink en layer(utilities) si se especificaba @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, inclúyelas siempre como una capa de orden superior a los componentes con los que deseas anularlas. Podrías pensar: “Acabo de agregar esta clase .pink para cambiar el color y no se aplica”.

Más información sobre las capas en cascada

También puedes consultar estos recursos para obtener más información sobre las capas en cascada: