Cascade Layers
Chrome99+, Firefox97+, Safari Technology Preview 및 Safari iOS에 도입된 CSS의 새로운 스펙 Cascade Layers에 대해 정리했습니다.
이해
a. CSS Cascade
CSS Cascade는 CSS의 가장 강력한 강점으로, CSS가 요소에 적용하려는 스타일의 우선순위를 정하는 데 사용하는 알고리즘입니다. CSS Cascade는 어떤 스타일을 적용할지 결정하기 위해 몇 가지 기준을 고려하는데, 그 기준은 다음과 같습니다.
- Origin and Importance
- Context
- Style Attribute
- Specificity(명시도)
- Order of Apperance(작성 순서)

CSS Cascade 기준은 높은 순위에서 낮은 순위로 순위가 매겨지며, 스타일이 각 기준에 따라 비교되었을 때 동등한 기준일 경우, 다음 기준으로 넘어가 비교합니다.
개발자는 CSS를 작성할 때 명시도(Specificity) 와 작성 순서(Order of Appearance) 를 신중하게 고려해야합니다. 만약 적절한 코드 구성 계획이 없으면 아래와 같이 Cascade가 불리하게 작용할 수 있습니다.
- 선택자를 과다하게 사용할 경우, 코드의 일부 속성을 재정의할 때 더 무거운 선택자의 사용이나
!important의 사용으로 이어지며 이는 나중에 더 큰 문제를 야기할 수 있습니다. - 선택자를 적게 사용할 경우, 나중에 사용하는 명령문으로 너무 쉽게 덮어쓸 수 있습니다.
개발자가 이러한 딜레마를 쉽게 제어할 수 있도록 CSS Cascade 알고리즘에 Cascade Layers라는 새로운 개념이 도입됩니다.
※ 더 이상 사용되지 않는 layer태그와 이름이 비슷하여 혼동하기 쉽지만, 다른 개념입니다.
b. Cascade Layers
Cascade Layers는 CSS Cascade기준에서 Layers라는 새로운 기준을 추가합니다.
- Origin and Importance
- Context
- Style Attribute
- Layers
- Specificity(명시도)
- Order of Apperance(작성 순서)

Layers의 진정한 힘은 명시도(Specificity)와 작성 순서(Order Of Appearance)보다 더 높은 기준에 있다는 점에서 나옵니다.
Layers는 보다 상위 등급의 기준이므로, Layers 기준 내에서 스타일의 우선권이 정해지면 Cascade는 해당 스타일에 대한 명시도와 작성 순서를 더이상 체크하지 않습니다.
그 때문에 우리는 다른 Layers에서 사용되는 CSS의 명시도와 작성 순서에 대해 걱정할 필요가 없습니다.
또한 개발자는 Cascade Layers를 사용하여 CSS를 여러 레이어로 분할할 수 있습니다. 그리고 적용할 레이어 순서를 정할 수 있습니다. 그렇기 때문에 이 Layers가 CSS를 로드하는 순서에 대해 전혀 걱정할 필요가 없습니다.
/* Cascade Layers 선언 예시 코드 */
/* 레이어 순서 정의 - 1. reset, 2. base, 3. theme */
@layer reset, base, theme;
/* 첫번째 레이어 “reset” */
@layer reset { … }
/* 2번째 레이어 “base” */
@layer base { … }
/* 3번째 레이어 “theme” */
@layer theme { … }
구문
a. Cascade Layers 선언
기본 구문
@layer [ <layer-name># | <layer-name>? {
<stylesheet>
} ]
@layer블록에서 즉시 할당된 스타일 사용
/* reset이름의 layer 생성, 아래의 스타일 적용 */
@layer reset {
* {
margin: 0;
padding: 0;
}
}
@layer에서 스타일을 작성하지 않고 선언
/* reset이름의 layer 생성 */
@layer reset;
/* base, theme, util 레이어 생성 */
@layer base, theme, util;
@import 및 layer키워드 혹은 layer()함수를 사용하여 선언
/* reset.css파일 전체가 이름없는 layer로 생성 */
@import url(reset.css) layer;
/* base.css파일 전체가 base 이름의 layer에 스타일이 추가 */
@import url(base.css) layer(base);
b. Layers 순서 관리
Cascade Layers는 선언된 순서에 따라 정렬됩니다.

/* Layers 순서 예시 */
@layer reset { /* 첫번째 레이어 “reset” */
* {
margin: 0;
padding: 0;
}
}
/* 2번째 레이어 “base” */
@layer base { … }
/* 3번째 레이어 “theme” */
@layer theme { … }
/* 4번째 레이어 “util” */
@layer util { … }
순서대로 reset, base, theme, util 레이어가 생성됩니다. 레이어가 늦게 생성될수록 스타일 우선권을 가지게 됩니다.
c. Layer 이름
레이어의 이름은 레이어를 구별할 수 있는 고유한 식별자이지만, 이름을 정하는 것은 선택사항입니다.
이름을 지정하지 않은 레이어는 익명 레이어라고 합니다. 익명 레이어는 식별되지 않기 때문에 다른 위치에서 참조할 수 없습니다.
/* reset.css 파일 전체가 익명 레이어1 로 생성 */
@import url(reset.css) layer;
/* base.css 파일 전체가 익명 레이어2 로 생성 */
@import url(base.css) layer;
/* 아래의 스타일을 가지는 익명 레이어3 생성 */
@layer {
/* the next layer */
}
/* 아래의 스타일을 가지는 익명 레이어4 생성 */
@layer {
/* and another */
}
레이어 이름 재사용
이미 선언한 레이어 이름을 재사용할 경우, 기존에 존재하던 레이어에 스타일이 덧붙여 추가됩니다. 레이어의 적용 순서는 변하지 않습니다.
/* 첫번째 레이어 “reset” */
@layer reset { … }
/* 2번째 레이어 “base” */
@layer base { … }
/* 3번째 레이어 “theme” */
@layer theme { … }
/* 2번째 레이어인 “base”에 스타일 추가 */
@layer base {
* {
color : red;
}
}
그렇기 때문에 레이어 순서를 계속해서 제어하려면 한 줄 구문을 사용하여 모든 레이어를 미리 선언하여 순서를 결정하고, 이후 스타일을 추가하는 것이 권장됩니다.
/* 선언 순서에 따라 1. reset, 2.base, 3. theme, 4.util */
@layer reset, base, theme, util;
/* 첫번째 레이어 “reset” */
@layer reset { … }
/* 네번째 레이어 “util” */
@layer util { … }
/* 두번째 레이어 “base” */
@layer base { … }
~
d. revert-layer
속성의 값에 revert-layer를 사용할 수 있습니다. revert-layer는 이전 계층에서 정의한 값으로 롤백하기 위한 값입니다. 즉, 현재 레이어의 값이 지정되지 않은 것처럼 계산합니다.
@layer default {
h3 { color: rebeccapurple; }
}
@layer theme {
h3 { color: maroon; }
/* h3태그에 no-theme클래스가 추가되어있으면, revert-layer값으로 인해 color가 이전 레이어인 default의 스타일인 rebeccapurple가 적용됩니다. */
.no-theme { color: revert-layer; }
}
활용
a. Layers 중첩
@layer명령문을 중첩해서 사용할 수 있습니다. 중첩된 레이어 내부에서 레이어 우선순위는 기존 레이어와 동일하게 작용합니다.

/* 중첩 레이어 예시 */
@layer base { /* 첫번째 레이어 */
p { max-width: 70px; }
}
@layer framework { /* 두번째 레이어 */
@layer base { /* 두번째 레이어의 첫번째 레이어 */
p { max-width: 100px; }
}
@layer theme { /* 두번째 레이어의 두번째 레이어 */
p { color: #222; }
}
}
예시 레이어의 순서는 다음과 같습니다.
baseframeworkbasetheme
만약 레이어 안에 포함된 레이어를 참조하려면 마침표 .를 이용하여 계층 구조를 참조할 수 있습니다.
@layer framework {
@layer theme {
p { color: #222; }
}
}
/* framework layer안의 theme layer를 참조하고 속성을 추가 */
@layer framework.theme {
blockquote { color: rebeccapurple; }
}
b. Unlayered Style
@layer를 사용하지 않은 스타일은 Unlayered Styles라고 하며 레이어의 순서 마지막에 위치합니다.
그렇기 때문에 레이어가 없는 스타일은 항상 레이어가 있는 스타일보다 우선권을 가집니다.

/* 첫번째 layer reset */
@import(reset.css) layer(reset);
/* layer 없는 스타일 - Unlayered Styles */
h1 { color: hotpink; }
/* 두번째 layer base */
@layer base {
h1 { font-family: 돋움; }
}
/* layer 없는 스타일 - Unlayered Styles */
h1 { font-family: 굴림; }
/* 3rd layer */
@layer theme {
body h1 { color: rebeccapurple; }
}
예시의 레이어 순서는 다음과 같습니다.
resetbasetheme(Unlayered Styles)
따라서 모든 h1태그들의 color속성은 hotpink색이되고, font-family는 굴림이 됩니다.
c. Layers 미디어쿼리
미디어쿼리 안에 @layer를 사용할 수 있습니다. 하지만 미디어쿼리가 조건이 충족되지 않으면 레이어 순서에 고려되지 않습니다. 조건에 충족될 때 레이어 순서가 다시 계산됩니다.
@media (min-width: 30px) {
@layer layout {
.title { font-size: x-large; }
}
}
@media (prefers-color-scheme: dark) {
@layer theme {
.title { color: white; }
}
}
해당 예시에서 첫 번째 미디어쿼리가 일치하는 경우 layout이 레이어 순서에 먼저 표기되고, 두 번째 미디어쿼리도 일치할 경우 그다음 순서에 theme이 추가됩니다. 쿼리문이 충족되지 않으면 레이어 순서에서 사라집니다.
주의사항
a. Cascade Layers에서 !important사용
Cascade의 기준 중 첫 번째 기준인 Origin기준을 평가할 때, Cascade는 여러 Origin을 다음과 같이 정렬합니다.
- Transition
- Important User-Agent
- Important User
- Important Author
- Animations
- Author
- User
- User-Agent
선언이 !important로 표시되면 Cascade에서 가중치가 증가하고 우선순위가 반전됩니다.
이 규칙은 Cascade Layers의 선언에도 똑같이 적용이 됩니다.

/* 예시 레이어 reset, base, theme, utilities */
@layer reset, base, theme, utilities;
위 예시 레이어들의 일반 선언은 모두 Normal Author Origin으로 이동하며 다음과 같이 정렬됩니다.
resetbasethemeutilities
해당 레이어들 내부의 important선언은 모두 Important Author Origin으로 이동하며 순위가 반전됩니다.
따라서 해당 레이어들의 우선순위는 아래와 같습니다.
- important Author Origin
- important
utilities - important
theme - important
base - important
reset
- important
- Animations
- Normal Author Origin
resetbasethemeutilities
b. 인터리빙 불가
@layer CSS parser가 이전 규칙을 따르는 것을 보는 순간, 이후의 @import규칙은 적용되지 않습니다. 즉, @import, @namespace규칙과 @layer는 서로 인터리빙 할 수 없습니다.
@layer default;
@import url(theme.css) layer(theme);
/* @import 뒤에 @layer규칙이 나왔으므로 @import규칙은 여기서 종료됩니다. */
@layer components;
/* 서로 인터리빙이 되지 않기 때문에 이후에 나오는 모든 @import규칙들은 무시됩니다. */
@import url(default.css) layer(default);
@layer default {
audio[controls] {
display: block;
}
}
따라서 이를 잘 사용하려면 정해진 형식을 갖추고 그 형식에 맞게 코드를 작성하는 것이 권장됩니다.
@layer문 at-rules를 사용하여 사전에 레이어 순서를 결정@import를 그룹화@layerblock at-rules를 사용하여 스타일 추가
/* 1. 레이어의 순서를 결정 */
@layer default, theme, components;
/* 2. @import 그룹화 */
@import url(theme.css) layer(theme);
@import url(default.css) layer(default);
/* 3. @layer 스타일 추가 */
@layer default {
audio[controls] {
display: block;
}
}
@layer theme { … }
~
브라우저 지원

업데이트 사항
a. Unlayered Style issue#6284
이전 버전의 Cascade Layer에서는 Unlayered Style이 가장 우선권이 약한 스타일로 지정이 되었습니다. 하지만 레이어의 주요 사용 사례는 재설정 혹은 리팩토링 및 프레임워크 테마입니다. Layered style 순위 위에 Unlayered style이 있을 경우 레이어를 고려해 규칙 순서가 우선 순위와 일치하도록 파일을 다시 작성해야하기 때문에 가장 우선권이 강한 스타일로 바뀌게 되었습니다.
b. @layer문법 변경 issue#5681
/* @layer <name>? url(<contents>) */
@layer box url(box.css);
@layer url(reset.css);
이전 버전의 Cascade Layers에서는 @layer 와 url()를 사용해서 레이어를 선언할 수 있었으나, 문법문제로 인하여 @layer에서는 url()을 사용하지 못하고 @import에서만 사용가능하게 변경되었습니다.
3개의 댓글
김보미 · 2022년 5월 25일 10:09 오전
정리가 잘 된 좋은 글 감사합니다! 덕분에 이해가 쏙쏙 잘 되었습니다 🙂
두루미 · 2022년 5월 26일 9:52 오전
좋은 글 고맙습니다~
이상호 · 2022년 5월 28일 11:12 오후
좋은 글 감사합니다~