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.
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.
@layer
em ação
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.
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.
Camadas e a cascata
Vamos voltar um pouco e ver onde as camadas são usadas com relação à cascata mais ampla:
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: