As camadas em cascata (a regra @layer
CSS) estão chegando ao 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 é útil principalmente para bases de código grandes, sistemas de design e ao gerenciar estilos de terceiros em aplicativos.
A organização do CSS de maneira clara evita substituições de estilo inesperadas e promove uma melhor arquitetura de CSS.
Especificidade do CSS e a cascata
A especificidade do CSS é a forma 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 que as classes ou atributos, que, por sua vez, são menos específicos que os IDs. Essa é uma parte essencial do aprendizado de CSS.
As pessoas recorrem a convenções de nomenclatura CSS, como o BEM, para evitar a substituição involuntária da especificidade. Ao atribuir uma única classe de nome, tudo é colocado no mesmo plano de especificidade. No entanto, nem sempre é possível manter esses estilos organizados, especialmente ao trabalhar com códigos e sistemas de design de terceiros.
As camadas em cascata têm como objetivo resolver esse problema. Eles introduzem uma nova camada na 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 maior especificidade que .card a
. Se você tentar estilizar um link dentro de um card em uma postagem, o seletor mais específico será aplicado.
Ao usar @layer
, você pode ser mais explícito sobre a especificidade de 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 todo o CSS estiver no mesmo plano. Isso ocorre devido à precedência em cascata. Os estilos em camadas criam novos "planos" em cascata.
@layer
em ação
Este exemplo mostra o poder das camadas em cascata usando @layer
. Vários links são mostrados: alguns sem nenhum nome de classe adicional aplicado, um com uma classe .link
e outro com uma classe .pink
. O CSS adiciona três camadas: base
, typography
e utilities
, conforme mostrado abaixo:
@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 */
}
}
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
em um @layer
de precedência maior. 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 tem prioridade sobre a especificidade do elemento.
Como organizar camadas
Você pode organizar as camadas diretamente na página, como mostrado acima, ou na parte de cima de um arquivo.
A ordem das camadas é estabelecida pela primeira vez que o nome de cada camada aparece no código.
Isso significa que, se você adicionar o seguinte ao início do arquivo, todos os links vão aparecer em vermelho, e o link com a classe .link
vai aparecer em azul:
@layer utilities, typography, base;
Isso ocorre porque a ordem das camadas agora está invertida, colocando os utilitários em primeiro lugar e a base em ú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, mas vermelhos ou azuis.
Como organizar importações
Outra maneira de usar @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 em 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 suas 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
seus estilos não importa para a ordem da camada, já que ela já está estabelecida na primeira instância do nome da camada. Isso é uma preocupação a menos. Ainda é possível definir arquivos importados para camadas específicas, mas a ordem já está estabelecida.
Camadas e a cascata
Vamos dar um passo atrás e conferir onde as camadas são usadas em 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
- Autor @layers
- Autor normal
- Autor !important
- Autor @layer !important
- Local User !important
- User Agent !important** (maior precedência)
Os estilos @layer !important
estão invertidos. Em vez de serem menos específicos do que os estilos sem camadas (normais), eles têm precedência maior. Isso ocorre devido à forma como !important
funciona na cascata: ele interrompe a cascata normal nas 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 camadas. O exemplo a seguir é do explicador de camadas em cascata 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
aninhada em framework
. Também é possível escrever isso em um formato mais abreviado:
@layer framework.default {
p { margin-block: 0.75em }
}
As camadas e a ordem resultantes 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 e resultados inesperados. Ao trabalhar com camadas em cascata, observe o seguinte:
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 um @layer
, por exemplo, card.css
, e quiser estilizar todos os links no card, não escreva estilos como:
a {
…
}
Isso vai fazer com que todas as tags a
no arquivo recebam essa substituição. Ainda é importante especificar seus estilos corretamente:
.card a {
…
}
Regra 2: as camadas em cascata são ordenadas atrás de CSS sem camadas
É importante observar que um arquivo CSS em camadas não substitui o CSS sem camadas. Essa foi uma decisão intencional para facilitar a introdução de camadas de uma maneira mais sensata para trabalhar com a base de código atual. O uso de um arquivo reset.css
, por exemplo, é um bom ponto de partida e um caso de uso para camadas em cascata.
Regra 3: !important
inverte a especificidade da cascata
Embora os estilos em camadas sejam menos específicos do que os estilos sem camadas em geral, o uso de !important
inverte isso. Em uma camada, as declarações com a regra !important
são mais específicas do 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 do que as normais, que têm menos precedência do que as !important, que têm menos precedência do que as @layer !important.
Se você tiver várias camadas, a primeira camada com !important
terá precedência sobre !important
e será o estilo mais específico.
Regra 4: entender os pontos de injeção
Como a ordem das camadas é estabelecida pela primeira vez que cada nome de camada aparece no código, se você colocar uma declaração @layer
após importar e definir layer()
ou após 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 para camadas em cascata, a ordem é estabelecida na primeira instância.
Isso pode ser em uma lista, em um bloco de camadas ou em uma importação. Se você colocar @layer
após uma lista de importação com layer()
, nada vai acontecer. Colocá-lo na parte de cima do arquivo vai definir a ordem das camadas e ajudar a visualizar claramente as camadas na arquitetura.
Regra nº 5: preste atenção à especificidade
Com as camadas em cascata, um seletor menos específico (como a
) vai substituir um seletor mais específico (como .link
) se ele estiver em uma camada mais específica. Considere 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 por isso.
Portanto, se você estiver escrevendo classes utilitárias, sempre as inclua como uma camada de ordem superior aos componentes que pretende substituir. Você pode pensar: "Acabei de adicionar essa classe .pink
para mudar a cor, mas 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: