Camadas em cascata estão chegando ao seu navegador

As camadas em cascata (a regra CSS @layer) vão ser lançadas no Chromium 99, Firefox 97 e Safari 15.4 Beta. Elas permitem um controle mais explícito dos arquivos CSS para evitar conflitos de especificidade de estilo. Isso é particularmente útil para grandes bases de código, sistemas de design e para gerenciar estilos de terceiros em aplicativos.

A criação de camadas CSS de maneira clara evita modificações de estilo inesperadas e promove uma arquitetura CSS melhor.

Especificidade do CSS e a cascata

A especificidade do CSS é como o CSS decide quais estilos aplicar a quais elementos. Os diferentes seletores que você pode usar determinam a especificidade de qualquer regra de estilo. Por exemplo, os elementos são menos específicos do que as classes ou os atributos, que, por sua vez, são menos específicos do que os IDs. Essa é uma parte fundamental do aprendizado de CSS.

As pessoas recorrem às convenções de nomenclatura do CSS, como o BEM, para evitar modificar a especificidade involuntariamente. Ao dar a tudo um único nome de classe, tudo é colocado no mesmo plano de especificidade. No entanto, nem sempre é possível manter esses estilos organizados, especialmente ao trabalhar com código e sistemas de design de terceiros.

Visual BEM de um card com classes
Um exemplo ilustrado de nomenclatura BEM de keepinguptodate.com.

O objetivo das camadas em cascata é resolver esse problema. Eles introduzem uma nova camada à cascata do CSS. Com estilos em camadas, a precedência de uma camada sempre supera a especificidade de um seletor.

Por exemplo, o seletor .post a.link tem especificidade maior que .card a. Se você tentar estilizar um link em um card, em uma postagem, você verá que o seletor mais específico será aplicado.

Ao usar @layer, você pode detalhar mais a especificidade do estilo de cada um e garantir que os estilos do link do card substituam os estilos do link da postagem, mesmo que a especificidade seja numericamente menor se todos os CSSs estiverem no mesmo plano. Isso ocorre devido à precedência em cascata. Estilos em camadas criam novos "planos" em cascata.

Ilustração da demonstração do projeto que mostra como detalhar a IU

@layer em ação

Demonstração de como mostrar cores de links com importações
Consulte a demonstração no Codepen.

Esse exemplo mostra o poder das camadas em cascata usando @layer. Vários links são exibidos: alguns sem nomes de classe adicionais, um com uma classe .link e outro com uma .pink. Em seguida, o CSS adiciona três camadas: base, typography e utilities, da seguinte maneira:

@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 */
  }
}

Por fim, todos os links são verdes ou rosa. Isso ocorre porque: embora .link tenha uma especificidade maior no nível do seletor do que a, há um estilo de cor em a com uma precedência maior de @layer. a { color: green } substitui .link { color: blue } quando a regra verde está em uma camada após a regra azul.

A precedência da camada supera a especificidade do elemento.

Como organizar camadas

Você pode organizar as camadas diretamente na página, como mostrado acima, ou organizá-las na parte superior de um arquivo.

A ordem das camadas é estabelecida na primeira vez em que o nome de cada camada aparece no código.

Isso significa que, se você adicionar o código abaixo à parte de cima do arquivo, todos os links vão aparecer em vermelho e o link com a classe .link, em azul:

@layer utilities, typography, base;

Isso ocorre porque a ordem das camadas agora é invertida, colocando os utilitários primeiro e a base por último. Portanto, as regras de estilo na camada base sempre terão uma especificidade maior do que as regras de estilo na camada de tipografia. Eles não serão mais links verdes, e sim vermelhos ou azuis.

Captura de tela do projeto Codepen
Consulte a demonstração no Codepen.

Como organizar importações

Outra maneira de usar o @layer é com arquivos de importação. É possível fazer isso diretamente ao importar estilos usando uma função layer(), como no exemplo a seguir:

/* 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 */

O snippet de código acima tem três camadas: base, layouts e components. Os arquivos de normalização, tema e tipografia no base, com um arquivo post em layouts, e cards e footer em components. Na importação do arquivo, as camadas são instanciadas usando a função de camada. Uma abordagem alternativa seria organizar as camadas na parte de cima do arquivo, declarando-as antes de qualquer importação:

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

Agora, a ordem em que você @import os estilos não importa para a ordem das camadas, já que ela já está estabelecida na primeira instância do nome da camada. É uma coisa a menos para se preocupar. Ainda é possível definir arquivos importados para camadas específicas, mas a ordem já está estabelecida.

Captura de tela do projeto Codepen
Conheça o projeto no Codepen.

Camadas e a cascata

Vamos voltar um pouco e ver onde as camadas são usadas com relação à cascata mais ampla:

Ilustração em cascata

A ordem de precedência é a seguinte:

  • User agent normal (menor precedência)
  • Usuário local @layer
  • Usuário local normal
  • Autoria em @layers
  • Normal do autor
  • Autor: !important
  • Autor @layer !important
  • Usuário local !important
  • User agent !important** (prioridade mais alta)

Você pode notar que os estilos @layer !important estão invertidos. Em vez de serem menos específicos do que os estilos sem camadas (normais), eles têm maior precedência. Isso ocorre devido à forma como !important funciona na cascata: ele quebra a cascata normal nas suas folhas de estilo e inverte a especificidade normal no nível da camada (precedência).

Camadas aninhadas

As camadas também podem ser aninhadas dentro de outras. O exemplo a seguir vem da explicação "Cascade Layers" de Miriam Suzanne:

@layer default {
  p { max-width: 70ch; }
}

@layer framework {
  @layer default {
    p { margin-block: 0.75em; }
  }

  p { margin-bottom: 1em; }
}

No snippet de código acima, é possível acessar framework.default usando um . como um indicador da camada default que está sendo aninhada em framework. Você também pode escrever isso em um formato mais curto:

@layer framework.default {
  p { margin-block: 0.75em }
}

As camadas resultantes e a ordem delas são:

  • padrão
  • framework.default
  • framework sem camadas
  • sem camadas

Preste atenção nos seguintes itens

As camadas em cascata podem ser ótimas se usadas corretamente, mas também podem criar confusão adicional e resultados inesperados. Considere o seguinte ao trabalhar com camadas em cascata:

Regra 1: não use @layer para definir o escopo

As camadas em cascata não resolvem o escopo. Se você tiver um arquivo CSS com @layer, como card.css e quiser estilizar todos os links do cartão, não escreva estilos como:

a {
  …
}

Isso fará com que todas as tags a no seu arquivo recebam essa substituição. Ainda é importante definir o escopo dos estilos corretamente:

.card a {
  …
}

Regra 2: as camadas em cascata são ordenadas por trás do CSS sem camadas

Um arquivo CSS em camadas não substitui um CSS sem camadas. Essa foi uma decisão intencional para facilitar a introdução de camadas de uma maneira mais sensata de trabalhar com sua base de código atual. Usar um arquivo reset.css, por exemplo, é um bom ponto de partida e caso de uso para camadas em cascata.

Regra 3: !important inverte a especificidade em cascata

Embora os estilos em camadas sejam menos específicos do que os estilos sem camadas em geral, o uso de !important reverte isso. Em uma camada, as declarações com a regra !important são mais específicas que os estilos sem camadas.

Nesse caso, os estilos !important invertem a especificidade. O diagrama acima mostra isso para referência: as @layers do autor têm menos precedência que a do autor normal, que têm menos precedência do que a do autor !important, que tem menos precedência que a do autor @layer !important.

Se você tiver várias camadas, a primeira com !important terá a precedência de !important e será o estilo mais específico.

Regra 4: entenda os pontos de injeção

Como a ordem das camadas é estabelecida na primeira vez que cada nome de camada aparece no código, se você colocar uma declaração @layer após importar e definir as layer() ou depois de uma instrução @layer diferente, ela poderá ser ignorada. Ao contrário do CSS, em que a regra de estilo mais abaixo na página é aplicada a camadas em cascata, a ordem é estabelecida na primeira instância.

Isso pode estar em uma lista, em um bloco de camadas ou em uma importação. Se você colocar @layer depois de uma lista de importação com layer(), nada acontecerá. Colocar essa API na parte de cima do arquivo define a ordem das camadas e ajuda a ver claramente as camadas na arquitetura.

Regra N.o 5: observe sua especificidade

Com as camadas em cascata, um seletor menos específico (como a) vai substituir um mais específico (como .link) se ele estiver em uma camada mais específica. Recomendamos o seguinte:

a em layer(components) substituiria .pink em layer(utilities) se @layer utilities, components fosse especificado. Embora seja uma parte intencional da API, isso pode ser confuso e frustrante se você não estiver esperando isso.

Portanto, se você estiver criando classes de utilitários, sempre as inclua como uma camada de ordem superior do que os componentes com os quais você pretende substituí-las. Talvez você pense: "Acabei de adicionar essa classe .pink para mudar a cor, e ela não está sendo aplicada".

Saiba mais sobre as camadas em cascata

Confira também estes recursos para saber mais sobre as camadas em cascata: