Você sempre pode obter uma visão geral fantástica das coisas no artigo de Stephenie Eckles, “Getting Started With CSS Cascade Layers”. Mas vamos falar sobre a experiência de integração de camadas em cascata no código do mundo real, o bom, o ruim e o espaguete.
Eu poderia ter criado um projeto de exemplo para um passo a passo clássico, mas não, não é assim que as coisas funcionam no mundo real. Quero sujar as mãos, como herdar código com estilos que funcionam e ninguém sabe por quê.
Encontrar projetos sem camadas em cascata foi fácil. A parte complicada foi encontrar um que fosse confuso o suficiente para ter problemas de especificidade e organização, mas amplo o suficiente para ilustrar diferentes partes da integração de camadas em cascata.
Senhoras e senhores, apresento a vocês este site de bot Discord de Drishtant Ghosh. Estou profundamente grato a Drishtant por me permitir usar seu trabalho como exemplo. Este projeto é uma landing page típica com uma barra de navegação, uma seção hero, alguns botões e um menu móvel.
Você vê como parece perfeito por fora. As coisas ficam interessantes, entretanto, quando olhamos para os estilos CSS subjacentes.
Compreendendo o projeto
Antes de começarmos a jogar @layers por aí, vamos entender melhor com o que estamos trabalhando. Clonei o repositório GitHub, e como nosso foco é trabalhar com CSS Cascade Layers, vou focar apenas na página principal, que consiste em três arquivos: index.html, index.csse index.js.
Observação: Não incluí outras páginas deste projeto, pois tornaria este tutorial muito detalhado. No entanto, você pode refatorar as outras páginas como uma experiência.
O index.css O arquivo tem mais de 450 linhas de código e, folheando-o, posso ver alguns sinais de alerta logo de cara:
- Há muita repetição de código com os mesmos seletores apontando para o mesmo elemento HTML.
- Existem alguns
#idseletores, que se poderia argumentar que não deveriam ser usados em CSS (e eu sou uma dessas pessoas). #botLogoé definido duas vezes e com mais de 70 linhas de intervalo.- O
!importantpalavra-chave é usada liberalmente em todo o código.
E ainda assim o site funciona. Não há nada “tecnicamente” errado aqui, o que é outra razão pela qual o CSS é um monstro grande e lindo – os erros são silenciosos!
Planejando a estrutura da camada
Agora, alguns podem estar pensando, “Não podemos simplesmente mover todos os estilos para uma única camada, como @layer legacy e encerrar o dia?
Você poderia… mas não acho que deveria.
Pense nisso: se mais camadas forem adicionadas após a legacy camada, eles deve substituir os estilos contidos no legacy camada porque a especificidade das camadas é organizada por prioridade, onde as camadas declaradas posteriormente têm maior prioridade.
/* new is more specific */
@layer legacy, new;
/* legacy is more specific */
@layer new, legacy;
Dito isto, devemos lembrar que os estilos existentes no site fazem uso liberal do !important palavra-chave. E quando isso acontece, a ordem das camadas em cascata é invertida. Então, mesmo que as camadas estejam delineadas assim:
@layer legacy, new;
…qualquer estilo com um !important declaração de repente agita as coisas. Neste caso, a ordem de prioridade passa a ser:
!importantestilos nolegacycamada (mais poderosa),!importantestilos nonewcamada,- Estilos normais no
newcamada, - Estilos normais no
legacycamada (menos poderosa).
Eu só queria esclarecer essa parte. Vamos continuar.
Sabemos que as camadas em cascata lidam com a especificidade criando uma ordem explícita onde cada camada tem uma responsabilidade clara e as camadas posteriores sempre vencem.
Então, decidi dividir as coisas em cinco camadas distintas:
reset: O padrão do navegador é redefinido comobox-sizingmargens e preenchimentos.base: estilos padrão de elementos HTML, comobody,h1,p,aetc., incluindo tipografia e cores padrão.layout: Material principal da estrutura da página para controlar como os elementos são posicionados.components: segmentos de UI reutilizáveis, como botões, cartões e menus.utilities: modificadores auxiliares únicos que fazem apenas uma coisa e fazem bem.
É apenas assim que gosto de dividir as coisas e organizar estilos. Zell Liew, por exemplo, possui um conjunto diferente de quatro baldes que podem ser definidos como camadas.
Há também o conceito de dividir as coisas ainda mais em subcamadas:
@layer components {
/* sub-layers */
@layer buttons, cards, menus;
}
/* or this: */
@layer components.buttons, components.cards, components.menus;
Isso pode ser útil, mas também não quero abstrair demais as coisas. Essa pode ser uma estratégia melhor para um projeto cujo escopo é um sistema de design bem definido.
Outra coisa que poderíamos aproveitar é estilos sem camadas e o fato de que quaisquer estilos não contidos em uma camada em cascata recebem a prioridade mais alta:
@layer legacy { a { color: red !important; } }
@layer reset { a { color: orange !important; } }
@layer base { a { color: yellow !important; } }
/* unlayered */
a { color: green !important; } /* highest priority */
Mas gosto da ideia de manter todos os estilos organizados em camadas explícitas porque mantém as coisas modular e sustentávelpelo menos neste contexto.
Vamos prosseguir adicionando camadas em cascata a este projeto.
Integrando camadas em cascata
Precisamos definir a ordem das camadas no topo do arquivo:
@layer reset, base, layout, components, utilities;
Isso torna mais fácil saber qual camada tem precedência sobre qual (elas têm mais prioridade da esquerda para a direita), e agora podemos pensar em termos de responsabilidade da camada em vez de peso do seletor. Seguindo em frente, prosseguirei com a folha de estilo de cima para baixo.
Primeiro, notei que a fonte Poppins foi importada nos arquivos HTML e CSS, então removi a importação CSS e deixei aquela em index.htmlpois geralmente é recomendado para carregar fontes rapidamente.
Em seguida está o seletor universal (*), que incluem estilos clássicos de redefinição que são perfeitos para @layer reset:
@layer reset {
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
}
Com isso fora do caminho, o body o seletor é o próximo. Estou colocando isso em @layer base porque contém estilos básicos para o projeto, como planos de fundo e fontes:
@layer base {
body {
background-image: url("bg.svg"); /* Renamed to bg.svg for clarity */
font-family: "Poppins", sans-serif;
/* ... other styles */
}
}
A forma como estou abordando isso é que os estilos no base camada geralmente deve afetar todo o documento. Até agora, nenhuma quebra de página nem nada.
Troca de IDs para aulas
Seguindo o body seletor de elemento é o carregador de página, que é definido como um seletor de ID, #loader.
Acredito firmemente no uso de seletores de classe em vez de seletores de ID, tanto quanto possível. Ele mantém a especificidade baixa por padrão, o que evita batalhas de especificidade e torna o código muito mais fácil de manter.
Então, entrei no index.html arquivo e elementos refatorados com id="loader" para class="loader". No processo, vi outro elemento com id="page" e mudou isso ao mesmo tempo.
Enquanto ainda no index.html arquivo, notei alguns div elementos faltando tags de fechamento. É surpreendente como os navegadores são permissivos com isso. De qualquer forma, eu limpei tudo e mudei o tag out of the.headingelement to be a direct child ofbody.Let’s not make it any tougher to load our scripts.Now that we’ve levelled the specificity playing field by moving IDs to classes,we can drop them into thecomponentslayer since a loader is indeed a reusable component:
@layer components{.loader{width:100%;height:100vh}.loader.loading{}.loader.loading span{}.loader.loading span:before{}}Animations
Next are keyframes,and this was a bit tricky,but I eventually chose to isolate animations in their own new fifth layer and updated the layer order to include it:@layer reset,base,layout,components,utilities,animations;animationsas the last layer?Because animations are generally the last to run and shouldn’t be affected by style conflicts.I searched the project’s styles for@keyframesand dumped them into the new layer:@layer animations{@keyframes loading{}
@keyframes loading2{}
@keyframes pageShow{}}This gives a clear distinction of static styles from dynamic ones while also enforcing reusability.Layouts
The#pageselector also has the same issue as#id,and since we fixed it in the HTML earlier,we can modify it to.pageand drop it in thelayoutlayer,as its main purpose is to control the initial visibility of the content:@layer layout{.page{display:none}}Custom Scrollbars
Where do we put these?Scrollbars are global elements that persist across the site.This might be a gray area,but I’d say it fits perfectly in@layer basesince it’s a global,default feature.@layer base{::-webkit-scrollbar{width:8px}::-webkit-scrollbar-track{background:#0e0e0f}::-webkit-scrollbar-thumb{background:#5865f2;border-radius:100px}::-webkit-scrollbar-thumb:hover{background:#202225}}I also removed the!importantkeywords as I came across them.Navigation
Thenavelement is pretty straightforward,as it is the main structure container that defines the position and dimensions of the navigation bar.It should definitely go in thelayoutlayer:@layer layout{nav{display:flex;height:55px;width:100%;padding:0 50px}}Logo
We have three style blocks that are tied to the logo:nav.logo,.logo img,and#botLogo.These names are redundant and could benefit from inheritance component reusability.Here’s how I’m approaching it:- The
nav.logois overly specific since the logo can be reused in other places.I dropped thenavso that the selector is just.logo.There was also an!importantkeyword in there,so I removed it. - I updated
.logoto be a Flexbox container to help position.logo img,which was previously set with less flexible absolute positioning. - The
#botLogoID is declared twice,so I merged the two rulesets into one and lowered its specificity by making it a.botLogoclass.And,of course,I updated the HTML to replace the ID with the class. - The
.logo imgselector becomes.botLogo,making it the base class for styling all instances of the logo.
.botLogo{border-radius:50%;height:40px;border:2px solid #5865f2}.botLogo{border-radius:50%;width:180px}The difference is that one is used in the navigation and the other in the hero section heading.We can transform the second.botLogoby slightly increasing the specificity with a.heading.botLogoselector.We may as well clean up any duplicated styles as we go.Let’s place the entire code in thecomponentslayer as we’ve successfully turned the logo into a reusable component:@layer components{.logo{font-size:30px;font-weight:bold;color:#fff;display:flex;align-items:center;gap:10px}.botLogo{aspect-ratio:1;border-radius:50%;width:40px;border:2px solid #5865f2}.heading.botLogo{width:180px;height:180px;background-color:#5865f2;box-shadow:0px 0px 8px 2px rgba(88,101,242,0.5)}}Navigation List
This is a typical navigation pattern.Take an unordered list()and turn it into a flexible container that displays all of the list items horizontally on the same row(with wrapping allowed).It’s a type of navigation that can be reused,which belongs in thecomponentslayer.But there’s a little refactoring to do before we add it.There’s already a.mainMenuclass,so let’s lean into that.We’ll swap out anynav ulselectors with that class.Again,it keeps specificity low while making it clearer what that element does.@layer components{.mainMenu{display:flex;flex-wrap:wrap;list-style:none}.mainMenu li{margin:0 4px}.mainMenu li a{color:#fff;text-decoration:none;font-size:16px}.mainMenu li a:where(.active,.hover){color:#fff;background:#1d1e21}.mainMenu li a.active:hover{background-color:#5865f2}}There are also two buttons in the code that are used to toggle the navigation between “open” and “closed” states when the navigation is collapsed on smaller screens.It’s tied specifically to the.mainMenucomponent,so we’ll keep everything together in thecomponentslayer.We can combine and simplify the selectors in the process for cleaner,more readable styles:@layer components{nav:is(.openMenu,.closeMenu){font-size:25px;display:none;cursor:pointer;color:#fff}}I also noticed that several other selectors in the CSS were not used anywhere in the HTML.So,I removed those styles to keep things trim.There are automated ways to go about this,too.Media Queries
Should media queries have a dedicated layer(@layer responsive),or should they be in the same layer as their target elements?I really struggled with that question while refactoring the styles for this project.I did some research and testing,and my verdict is the latter,thatmedia queries ought to be in the same layer as the elements they affect.My reasoning is that keeping them together:- Maintains responsive styles with their base element styles,
- Makes overrides predictable,and
- Flows well with component-based architecture common in modern web development.
componentslayer:@layer components{.mainMenu{display:flex;flex-wrap:wrap;list-style:none}
@media(max-width:900px){.mainMenu{width:100%;text-align:center;height:100vh;display:none}}}This also allows me to nest a component’s child element styles(e.g.,nav.openMenuandnav.closeMenu).@layer components{nav{&.openMenu{display:none;@media(max-width:900px){&.openMenu{display:block}}}}}Typography&Buttons
The.titleand.subtitlecan be seen as typography components,so they and their responsive associates go into — you guessed it — thecomponentslayer:@layer components{.title{font-size:40px;font-weight:700}.subtitle{color:rgba(255,255,255,0.75);font-size:15px}
@media(max-width:420px){.title{font-size:30px}.subtitle{font-size:12px}}}What about buttons?Like many website’s this one has a class,.btn,for that component,so we can chuck those in there as well:@layer components{.btn{color:#fff;background-color:#1d1e21;font-size:18px}.btn-primary{background-color:#5865f2}.btn-secondary{transition:all 0.3s ease-in-out}.btn-primary:hover{background-color:#5865f2;box-shadow:0px 0px 8px 2px rgba(88,101,242,0.5)}.btn-secondary:hover{background-color:#1d1e21;background-color:rgba(88,101,242,0.7)}
@media(max-width:420px){.btn{font-size:14px;margin:2px;padding:8px 13px}}
@media(max-width:335px){.btn{display:flex;flex-direction:column}}}The Final Layer
We haven’t touched theutilitieslayer yet!I’ve reserved this layer for helper classes that are designed for specific purposes,like hiding content — or,in this case,there’s a.noselectclass that fits right in.It has a single reusable purpose:to disable selection on an element.So,that’s going to be the only style rule in ourutilitieslayer:@layer utilities{.noselect{-webkit-touch-callout:none;-webkit-user-select:none;-khtml-user-select:none;-webkit-user-drag:none;-moz-user-select:none;-ms-user-select:none;user-select:none}}And that’s it!We’ve completely refactored the CSS of a real-world project to use CSS Cascade Layers.You can compare where we started with the final code.It Wasn’t All Easy
That’s not to say that working with Cascade Layers was challenging,but there were some sticky points in the process that forced me to pause and carefully think through what I was doing.I kept some notes as I worked:- It’s tough to determine where to start with an existing project.However,by defining the layers first and setting their priority levels,I had a framework for deciding how and where to move specific styles,even though I was not totally familiar with the existing CSS.That helped me avoid situations where I might second-guess myself or define extra,unnecessary layers.
- Browser support is still a thing!I mean,Cascade Layers enjoy 94%support coverage as I’m writing this,but you might be one of those sites that needs to accommodate legacy browsers that are unable to support layered styles.
- It wasn’t clear where media queries fit into the process.Media queries put me on the spot to find where they work best:nested in the same layers as their selectors,or in a completely separate layer?I went with the former,as you know.
- The
!importantkeyword is a juggling act.They invert the entire layering priority system,and this project was littered with instances.Once you start chipping away at those,the existing CSS architecture erodes and requires a balance between refactoring the code and fixing what’s already there to know exactly how styles cascade.
Overall,refactoring a codebase for CSS Cascade Layers is a bit daunting at first glance.The important thing,though,is to acknowledge that it isn’t really the layers that complicate things,but the existing codebase.It’s tough to completely overhaul someone’s existing approach for a new one,even if the new approach is elegant.
Where Cascade Layers Helped(And Didn’t)
Establishing layers improved the code,no doubt.I’m sure there are someperformance benchmarksin there since we were able to remove unused and conflicting styles,but the real win is ina more maintainable set of styles.It’s easier to find what you need,know what specific style rules are doing,and where to insert new styles moving forward.At the same time,I wouldn’t say that Cascade Layers are a silver bullet solution.Remember,CSS is intrinsically tied to the HTML structure it queries.If the HTML you’re working with is unstructured and suffers fromdiv-itus,then you can safely bet that the effort to untangle that mess is higher and involves rewriting markup at the same time.Refactoring CSS for cascade layers is most certainly worth the maintenance enhancements alone.It may be “easier” to start from scratch and define layers as you work from the ground up because there’s less inherited overhead and technical debt to sort through.But if you have to start from an existing codebase,you might need to de-tangle the complexity of your styles first to determine exactly how much refactoring you’re looking at.
Fonte: Tecmundo, Olhar Digital, MeioBit

