Kodokon kodokon.com

Capas de cascada (@layer) y @scope

Domina los dos nuevos criterios de la cascada - las capas y la proximidad de ámbito - para estructurar tus estilos sin guerras de especificidad.

10 min · 3 preguntas

Abrir esta lección en Kodokon

La cascada resuelve las declaraciones en conflicto usando criterios ordenados: origen e importancia, luego el contexto, luego los estilos del atributo style, luego las capas (@layer), luego la especificidad, luego el orden de aparición. El punto crucial: las capas se evalúan antes que la especificidad. Una regla .button en una capa de mayor prioridad vence, por tanto, a #nav div.menu a.button en una capa de menor prioridad - su especificidad de (1,2,2) no cuenta para nada frente al orden de las capas. Eso es lo que hace que @layer sea tan potente para estructurar un sistema de diseño: la prioridad se declara una vez, de forma estructural, en lugar de tener que pelearla selector a selector.

CSS
@layer reset, base, components, utilities;

@layer components {
  .button { background: rebeccapurple; }
}

@layer base {
  button { background: gray; }
}
La declaración inicial fija el orden: components vence a base, sin importar el orden de los bloques en el archivo.

Dos sutilezas de la especificación que absolutamente necesitas conocer. Una: los estilos fuera de cualquier capa vencen a los estilos con capa (para las declaraciones normales) - forman una capa implícita colocada en último lugar. Tus sobrescrituras locales siempre vencen, por tanto, a la biblioteca con capas. Dos: con !important, el orden se invierte por completo. Las primeras capas declaradas vencen, y un !important dentro de una capa vence a un !important fuera de cualquier capa. Una declaración !important en tu capa reset se vuelve casi imbatible.

CSS
@layer base, theme;

@layer base {
  a { color: black !important; }
}

@layer theme {
  a { color: purple !important; }
}
Con !important, el orden se invierte: black vence porque base se declara primero.

@scope delimita un conjunto de reglas entre una raíz de ámbito y un límite inferior opcional: @scope (.card) to (.card__content) apunta a los elementos que son descendientes de .card pero están fuera de .card__content - el famoso donut scope, ideal para dar estilo al marco de un componente sin filtrarse en su contenido inyectado. Dentro, :scope se refiere a la raíz misma. Y @scope introduce un criterio de cascada totalmente nuevo: la proximidad. A igual especificidad, vence la regla cuya raíz de ámbito está más cerca del elemento - algo que ni BEM ni los combinadores pudieron nunca expresar.

CSS
@scope (.card) to (.card__content) {
  :scope {
    padding: 1rem;
    border: 1px solid oklch(0.85 0.02 260);
  }
  img { border-radius: 8px; }
}
Los elementos img del marco de .card quedan apuntados; los inyectados en .card__content quedan fuera de ámbito.

Prueba de conocimientos

Comprueba que has retenido los puntos clave de esta lección.

  1. Completa la declaración anticipada para que la capa components prevalezca sobre la capa base.
    @layer ___;
    
    @layer components {
      .button { background: purple; }
    }
    
    @layer base {
      .button { background: gray; }
    }
    • base, components
    • components, base
    • base components
  2. Una declaración normal fuera de cualquier capa se enfrenta a una declaración normal colocada en una capa. ¿Resultado?
    • La declaración con capa siempre vence
    • La declaración sin capa vence
    • Decide la especificidad del selector
    • El navegador reporta un conflicto
  3. En @scope (.card) to (.card__content), ¿a qué elementos pueden apuntar las reglas internas?
    • A todos los descendientes de .card sin excepción
    • A los descendientes de .card situados fuera de .card__content (donut scope)
    • Solo a .card__content y sus hijos