CSS 캐스케이드 레이어 통합 기존 프로젝트 – Smashing Magazine
Stephenie Eckles의 기사“CSS 캐스케이드 레이어 시작하기”에서 항상 환상적인 것들에 대한 개요를 얻을 수 있습니다. 그러나 캐스케이드 레이어를 실제 코드, 선, 악, 스파게티에 통합 한 경험에 대해 이야기합시다.
나는 고전적인 연습을위한 샘플 프로젝트를 만들 수 있었지만, Nah는 실제 세계에서 일이 작동하는 방식이 아닙니다. 나는 코드를 상속하는 스타일로 일하고 그 이유를 모르는 것처럼 우리의 손을 더럽 히고 싶습니다.
캐스케이드 레이어없이 프로젝트를 찾는 것은 쉬웠습니다. 까다로운 부분은 특이성과 조직 문제를 가질만큼 지저분한 부분을 찾는 것이었지만 캐스케이드 레이어 통합의 다른 부분을 설명 할만 큼 넓습니다.
신사 숙녀 여러분, Drishtant Ghosh의 Discord Bot 웹 사이트를 제시합니다. 나는 그의 작품을 예로들 수있게 해주신 Drishtant에게 깊이 감사합니다. 이 프로젝트는 내비게이션 바, 영웅 섹션, 몇 개의 버튼 및 모바일 메뉴가있는 일반적인 방문 페이지입니다.

당신은 그것이 외부에서 어떻게 완벽 해 보이는지 봅니다. 그러나 우리가 후드 아래의 CSS 스타일을 볼 때 상황이 흥미로워집니다.
프로젝트 이해
우리가 던지기 전에 @layers
주위에 우리가 무엇을하고 있는지에 대한 확실한 이해를 얻으 봅시다. Github Repo를 복제했으며 CSS 캐스케이드 레이어와 초점을 맞추고 있기 때문에 메인 페이지에만 집중할 것입니다. index.html
,,, index.css
그리고 index.js
.
메모: 이 프로젝트의 다른 페이지를 포함하지 않았기 때문에이 튜토리얼을 너무 장악하게 만들었습니다. 그러나 실험으로 다른 페이지를 리팩터링 할 수 있습니다.
그만큼 index.css
파일은 450 줄 이상의 코드 라인이며 그것을 통해 훑어 보면 박쥐에서 바로 붉은 깃발을 볼 수 있습니다.
- 동일한 선택기가 동일한 HTML 요소를 가리키는 코드 반복이 많이 있습니다.
- 꽤 많은 것이 있습니다
#id
CSS에서 사용해서는 안된다고 주장 할 수있는 셀렉터 (그리고 나는 그 사람들 중 하나입니다). #botLogo
두 번 및 70 줄 이상으로 정의됩니다.- 그만큼
!important
키워드는 코드 전체에서 자유롭게 사용됩니다.
그러나 사이트는 작동합니다. CSS가 크고 아름다운 괴물 인 또 다른 이유는 여기에 “기술적으로”잘못이 없습니다. 오류는 침묵합니다!
레이어 구조 계획
이제 일부는 생각하고있을 것입니다. “우리는 단순히 모든 스타일을 단일 레이어로 옮길 수 없습니다. @layer legacy
그리고 그것을 하루라고 부르나요?”
당신은 할 수 있습니다. 그러나 나는 당신이해야한다고 생각하지 않습니다.
그것에 대해 생각하십시오 : 더 많은 레이어가 추가되면 legacy
층 ~해야 한다 에 포함 된 스타일을 무시하십시오 legacy
층의 특이성은 우선 순위에 따라 구성되기 때문에 층이 나중에 선언 된 층이 나중에 우선 순위가 높아지기 때문입니다.
/* new is more specific */
@layer legacy, new;
/* legacy is more specific */
@layer new, legacy;
즉, 우리는 사이트의 기존 스타일이 !important
예어. 그리고 그런 일이 발생하면 캐스케이드 층의 순서가 반전됩니다. 따라서 레이어가 다음과 같이 요약되어 있어도 다음과 같습니다.
@layer legacy, new;
… !important
선언은 갑자기 일을 흔들었다. 이 경우 우선 순위 순서는 다음과 같습니다.
!important
스타일legacy
레이어 (가장 강력한),!important
스타일new
층,- 정상적인 스타일
new
층, - 정상적인 스타일
legacy
레이어 (가장 강력한).
나는 그 부분을 정리하고 싶었다. 계속합시다.
우리는 캐스케이드 레이어가 각 레이어가 명확한 책임을지는 명백한 순서를 만들어 특이성을 처리한다는 것을 알고 있으며, 나중에 레이어는 항상 승리합니다.
그래서 나는 물건을 다섯 가지 층으로 나누기로 결정했습니다.
reset
: 브라우저 기본 재설정과 같은box-sizing
여백과 패딩.base
: HTML 요소의 기본 스타일body
,,,h1
,,,p
,,,a
기본 타이포그래피 및 색상을 포함한 등.layout
: 요소의 위치를 제어하기위한 주요 페이지 구조.components
: 버튼, 카드 및 메뉴와 같은 재사용 가능한 UI 세그먼트.utilities
: 단 하나의 일을하고 잘하는 단일 도우미 수정 자.
이것은 단지 내가 물건을 깨고 스타일을 구성하는 방식 일뿐입니다. 예를 들어 Zell Liew는 레이어로 정의 될 수있는 4 개의 버킷 세트를 가지고 있습니다.
물건을 더욱 나누는 개념도 있습니다. 서브 레이어:
@layer components {
/* sub-layers */
@layer buttons, cards, menus;
}
/* or this: */
@layer components.buttons, components.cards, components.menus;
그것은 편리 할지도 모르지만 지나치게 추상적 인 것들을 원하지 않습니다. 그것은 잘 정의 된 디자인 시스템에 대한 프로젝트에 대한 더 나은 전략 일 수 있습니다.
우리가 활용할 수있는 또 다른 것은입니다 무인 스타일 그리고 캐스케이드 층에 포함되지 않은 모든 스타일이 우선 순위가 가장 높습니다.
@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 */
그러나 나는 모든 스타일을 명시 적 층으로 구성하는 아이디어가 마음에 들기 때문에 모듈 식 그리고 유지 가능적어도이 맥락에서.
이 프로젝트에 캐스케이드 레이어를 추가로 이동합시다.
캐스케이드 레이어 통합
파일 상단에서 레이어 순서를 정의해야합니다.
@layer reset, base, layout, components, utilities;
이를 통해 어떤 계층이 어떤 레이어가 우선 순위를 차지하는지 쉽게 알 수 있으며 (왼쪽에서 오른쪽으로 더 많은 우선 순위를 얻습니다), 이제 우리는 선택기 중량 대신 층 책임 측면에서 생각할 수 있습니다. 앞으로, 나는 스타일 시트를 위에서 아래로 진행합니다.
먼저, Poppins Font가 HTML 및 CSS 파일 모두에서 가져온 것을 알았으므로 CSS 가져 오기를 제거하고 그 중 하나를 index.html
글꼴을 빠르게로드하는 데 일반적으로 권장됩니다.
다음은 범용 선택기입니다.*
) 스타일은 @layer reset
:
@layer reset {
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
}
그 길에서 body
선택기는 다음입니다. 나는 이것을 입력하고있다 @layer base
배경 및 글꼴과 같은 프로젝트의 핵심 스타일이 포함되어 있기 때문입니다.
@layer base {
body {
background-image: url("bg.svg"); /* Renamed to bg.svg for clarity */
font-family: "Poppins", sans-serif;
/* ... other styles */
}
}
내가 이것을 다루는 방식은 base
레이어는 일반적으로 전체 문서에 영향을 미칩니다. 지금까지는 페이지가 나쁘거나 아무것도 없습니다.
클래스를 위해 ID를 바꾸는 것
다음 body
요소 선택기는 페이지 로더이며 ID 선택기로 정의됩니다. #loader
.
저는 최대한 ID 선택기를 통해 클래스 선택기를 사용하는 것을 확고한 신자입니다. 기본적으로 특이성을 낮게 유지하여 특이성 전투를 방지하고 코드를 훨씬 더 유지할 수있게합니다.
그래서 나는 들어갔다 index.html
파일 및 리팩토링 된 요소 id="loader"
에게 class="loader"
. 그 과정에서 나는 또 다른 요소를 보았다 id="page"
동시에 변경했습니다.
아직도 index.html
파일, 몇 가지를 발견했습니다 div
마감 태그 누락 요소. 허용 브라우저가 얼마나 놀라운 지 놀랍습니다. 어쨌든, 나는 그것들을 청소하고 움직였다 tag out of the
.heading
element to be a direct child of body
. 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 the components
layer 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;
animations
as 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 @keyframes
and 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#page
selector also has the same issue as #id
, and since we fixed it in the HTML earlier, we can modify it to .page
and drop it in the layout
layer, 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 base
since 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 !important
keywords as I came across them.
Navigation
Thenav
element 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 the layout
layer:
@layer layout {
/* ... */
nav {
display: flex;
height: 55px;
width: 100%;
padding: 0 50px; /* Consistent horizontal padding */
/* ... */
}
}
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 .logo
is overly specific since the logo can be reused in other places. I dropped thenav
so that the selector is just.logo
. There was also an!important
keyword in there, so I removed it. - I updated
.logo
to be a Flexbox container to help position.logo img
, which was previously set with less flexible absolute positioning. - The
#botLogo
ID is declared twice, so I merged the two rulesets into one and lowered its specificity by making it a.botLogo
class. And, of course, I updated the HTML to replace the ID with the class. - The
.logo img
selector becomes.botLogo
, making it the base class for styling all instances of the logo.
/* initially .logo img */
.botLogo {
border-radius: 50%;
height: 40px;
border: 2px solid #5865f2;
}
/* initially #botLogo */
.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 .botLogo
by slightly increasing the specificity with a .heading .botLogo
selector. We may as well clean up any duplicated styles as we go.
Let’s place the entire code in the components
layer 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; /* maintains square dimensions with width */
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 the components
layer. But there’s a little refactoring to do before we add it.
There’s already a .mainMenu
class, so let’s lean into that. We’ll swap out any nav ul
selectors 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 .mainMenu
component, so we’ll keep everything together in the components
layer. 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, that media 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.
components
layer:
@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 .openMenu
and nav .closeMenu
).
@layer components {
nav {
&.openMenu {
display: none;
@media (max-width: 900px) {
&.openMenu {
display: block;
}
}
}
}
}
Typography & Buttons
The.title
and .subtitle
can be seen as typography components, so they and their responsive associates go into — you guessed it — the components
layer:
@layer components {
.title {
font-size: 40px;
font-weight: 700;
/* etc. */
}
.subtitle {
color: rgba(255, 255, 255, 0.75);
font-size: 15px;
/* etc.. */
}
@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;
/* etc. */
}
.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);
/* etc. */
}
.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 theutilities
layer 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 .noselect
class 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 our utilities
layer:
@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
!important
keyword 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 some performance benchmarks in there since we were able to remove unused and conflicting styles, but the real win is in a 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.

Post Comment