1. 소개 및 누락된 섹션
이 문서는 CSS 계단식 스타일과 상속 5단계와의 차이 명세입니다. 현재는 탐색적 작업 초안입니다: 구현을 진행 중이라면 5단계를 참고하세요. 이 초안이 CR 단계에 도달하면 5단계의 내용을 병합할 예정입니다.
2. 계단식(Cascading)
계단식은 주어진 요소의 특정 속성에 대해 선언된 값의 무순서 목록을 받아, 아래에 설명된 선언의 우선순위에 따라 정렬하고, 단일 계단식 값을 산출합니다.
2.1. 계단식 정렬 순서
계단식은 선언을 우선순위가 높은 기준 순으로 아래 기준에 따라 정렬합니다:
- 출처와 중요성
-
선언의 출처는 어디서 왔는지에 따라 결정되며,
중요성은 !important로 선언되었는지 여부에 따라 결정됩니다(아래 참조).
다양한 출처의 우선순위는 다음과 같습니다(내림차순):
- 트랜지션 선언 [css-transitions-1]
- 중요한 사용자 에이전트 선언
- 중요한 사용자 선언
- 중요한 저자 선언
- 애니메이션 선언 [css-animations-1]
- 일반 저자 선언
- 일반 사용자 선언
- 일반 사용자 에이전트 선언
이 목록에서 앞부분 출처의 선언이 뒷부분 출처의 선언보다 우선합니다.
- 컨텍스트
-
문서 언어는 서로 다른 캡슐화 컨텍스트에서 비롯된 선언을 혼합할 수 있습니다.
예를 들어 트리 컨텍스트의 중첩(Shadow DOM의 섀도우 트리
등). [DOM]
서로 다른 캡슐화 컨텍스트에서 나온 두 선언을 비교할 때, 일반 규칙은 외부 컨텍스트의 선언이 우선하고, 중요한 규칙은 내부 컨텍스트의 선언이 우선합니다. 이때 [DOM] 트리 컨텍스트는 섀도우 포함 트리 순서로 중첩된 것으로 간주합니다.
참고: 일반 선언이 캡슐화 컨텍스트에 속할 경우 외부 컨텍스트에서 쉽게 덮어쓸 수 있는 기본값을 지정할 수 있고, 중요한 선언이 캡슐화 컨텍스트에 속할 경우 외부 컨텍스트에서 덮어쓸 수 없는 강제 요구사항을 지정할 수 있습니다.
- 스타일 속성
- 일반 및 중요한 선언 각각에 대해, 요소에 직접 부착된 선언(예: style 속성의 내용) 은 스타일 규칙 셀렉터를 통해 간접적으로 매핑된 선언보다 같은 중요성에서 더 높은 우선순위를 가집니다.
- 레이어
-
각 출처와 컨텍스트 내의 선언은 계단식 레이어에
명시적으로 할당될 수 있습니다.
이 단계에서 명시적으로 레이어가 할당되지 않은 선언은 암시적 마지막 레이어에 추가됩니다.
계단식 레이어(선언과 마찬가지로)는 등장 순서대로 정렬되며, § 2.4.1 레이어 순서 참조. 서로 다른 레이어에 속한 선언을 비교할 때, 일반 규칙에서는 레이어 순서에서 가장 마지막에 위치한 선언이 우선하고, 중요한 규칙에서는 가장 처음에 위치한 선언이 우선합니다.
참고: 이것은 일반 및 중요한 출처 우선순위와 동일한 논리를 따르므로, !important 플래그는 두 상황 모두에서 "오버라이드" 용도를 유지합니다.
- 특이성(Specificity)
- Selectors 모듈 [SELECT]은 셀렉터의 특이성을 계산하는 방법을 설명합니다. 각 선언은 해당 스타일 규칙의 특이성과 동일한 특이성을 가집니다. 특이성이 가장 높은 선언이 우선합니다.
- 범위 근접성
- 서로 다른 범위 루트가 있는 스타일 규칙의 선언을 비교할 때, 범위 루트와 범위 지정 스타일 규칙 대상 사이의 세대 또는 형제 요소 hop 수가 가장 적은 선언이 우선합니다. 이때 범위 루트가 없는 스타일 규칙은 무한 hop로 간주합니다.
- 등장 순서
-
문서상 마지막 선언이 우선합니다.
이때:
- 스타일 시트는 최종 CSS 스타일 시트와 동일한 순서로 정렬됩니다.
- import된 스타일 시트의 선언은 해당 스타일 시트가 @import 규칙 위치에 대체된 것처럼 정렬됩니다.
- 호스트 문서 언어가 결정한 링크 순서대로 독립적으로 연결된 스타일 시트의 선언들은 연결 순서대로 이어붙인 것처럼 처리합니다.
- 스타일 속성의 선언은 해당 요소의 문서상 등장 순서대로 정렬되며, 모든 스타일 시트 이후에 위치합니다. [CSSSTYLEATTR]
계단식의 결과는 각 요소의 각 속성에 대한 (비어있을 수도 있는) 선언된 값의 정렬된 목록입니다.
2.2. 계단식 출처
계단식 출처
2.3. 중요 선언: !important 어노테이션
CSS Cascading 5 § 6.3 중요 선언: !important 어노테이션
중요한 일반
2.4. 계단식 레이어
2.4.1. 레이어 순서
CSS Cascading 5 § 6.4.3 레이어 순서
2.5. 스타일 범위 지정: @scope 규칙
범위는 셀렉터가 좀 더 타겟팅된 매칭을 할 수 있도록 사용하는 문서의 서브트리 또는 프래그먼트입니다. 범위는 다음과 같이 결정하여 형성합니다:
다음 조건을 만족하면 요소는 범위 안에 있음으로 간주합니다:
-
해당 요소가 범위 루트의 포함 자손이고,
참고: 섀도우 캡슐화와 달리, 이는 DOM에서 섀도우 호스트와 중첩된 섀도우 트리 사이의 1:1 관계를 설명하지만, 동일한 요소에 대해 여러 겹치는 범위를 정의할 수 있습니다.
범위 지정 스타일은 CSS에서 @scope 블록 at-규칙을 사용하여 기술합니다. 이는 범위 루트와 선택적 범위 한계를 선언하며, 스타일 규칙 집합과 연결됩니다.
@scope ( .light-scheme) { /* light-scheme 내의 링크만 매칭 */ a{ color : darkmagenta; } } @scope ( .dark-scheme) { /* dark-scheme 내의 링크만 매칭 */ a{ color : plum; } } @scope ( .media-object) { /* media-object 내의 author 이미지만 매칭 */ .author-image{ border-radius : 50 % ; } }
@scope ( .media-object) to( .content > *) { img{ border-radius : 50 % ; } .content{ padding : 1 em ; } }
img 셀렉터는 DOM 프래그먼트 내에서 .media-object로 시작하여 .content 클래스의 자식까지 모든 자손을 포함하는 구조에서만 이미지 태그를 매칭합니다.
범위 한계를 범위 지정 셀렉터의 정의에 추가해야 할까요?
2.5.1. @scope의 효과
@scope at-규칙은 그 안에 포함된 스타일 규칙에 대해 세 가지 주요 효과를 가집니다:
-
@scope <rule-list> 내의 스타일 규칙은 범위 지정 스타일 규칙입니다.
-
:scope 셀렉터는 @scope 규칙의 범위 루트를 매칭하도록 정의됩니다. 특징 없는 섀도우 호스트가 범위 루트인 경우도 포함합니다. & 셀렉터는 범위 루트를 나타내는 셀렉터(<scope-start> 셀렉터)를 나타내거나, 셀렉터가 지정되지 않은 경우 :scope로 표기됩니다.
-
계단식은 더 가까운 범위 루트를 가진 선언을 우선합니다. 특이성이나 등장 순서와 무관하게 범위 근접성을 적용하여 범위 루트와 각 대상 범위 지정 스타일 규칙 사이를 비교합니다.
참고: Nesting과 달리, @scope 규칙 내의 셀렉터는 @scope 프렐류드의 부모 셀렉터 특이성을 상속하지 않습니다.
@scope ( #hero) { img{ border-radius : 50 % ; } } :where ( #hero) img{ border-radius : 50 % ; }
#hero 셀렉터의 추가 특이성은 범위 지정 셀렉터의 특이성에 적용되지 않습니다.
그러나 한
img
셀렉터가 범위 지정된 경우,
해당 셀렉터는 범위 근접성이 적용되어
계단식에서 더 높은 우선순위를 가집니다.
main-component
및 sub-component
)가 있고,
각 요소들은 data-scope
속성을 통해 한 범위 또는 두 범위 모두에 속함을 표시합니다:
< section data-scope = "main-component" > < p data-scope = "main-component" > ...< p > <!-- sub-component 루트는 두 범위 모두에 속함 --> < section data-scope = "main-component sub-component" > <!-- 자식들은 내부 범위에만 속함 --> < p data-scope = "sub-component" > ...< p > </ section > </ section >
이러한 커스텀 범위 속성들은 CSS의 모든 셀렉터에 추가됩니다:
p[ data-scope~='main-component' ] { color : red; } p[ data-scope~='sub-component' ] { color : blue; } /* 두 섹션 모두 외부 범위에 속함 */ section[ data-scope~='main-component' ] { background : snow; } /* 내부 섹션은 내부 범위에도 속함 */ section[ data-scope~='sub-component' ] { color : ghostwhite; }
@scope 규칙을 사용하면, 저자와 도구들은 고유 속성이나 클래스를 범위 루트에만 적용하여 유사한 동작을 구현할 수 있습니다:
< section data-scope = "main-component" > < p > ...< p > < section data-scope = "sub-component" > < p > ...< p > </ section > </ section >
그 후 클래스나 속성을 사용해 상한과 하한을 모두 설정할 수 있습니다. 하한 셀렉터에 매칭된 요소는 결과 범위에서 제외되어, 저자는 기본적으로 겹치지 않는 범위를 생성할 수 있습니다:
@scope ([ data-scope='main-component' ]) to([ data-scope]) { p{ color : red; } /* 외부 섹션만 외부 범위에 포함됨 */ section{ background : snow; } } @scope ([ data-scope='sub-component' ]) to([ data-scope]) { p{ color : blue; } /* 내부 섹션은 내부 범위에만 포함됨 */ section{ color : ghostwhite; } }
하지만 저자는 자식 결합자와 전체 선택자를 사용해 범위 경계를 겹치게 지정할 수 있으며, 이 경우 내부 범위 루트가 두 범위 모두에 속하게 됩니다:
@scope ([ data-scope='main-component' ]) to([ data-scope] > *) { p{ color : red; } /* 두 섹션 모두 외부 범위에 속함 */ section{ background : snow; } }
2.5.2. @scope의 문법
@scope 규칙의 문법은 다음과 같습니다:
@scope [(<scope-start>)]? [to (<scope-end>)]? { <rule-list> }
설명:
-
<scope-start>는 <selector-list> 셀렉터로, 범위 루트를 식별합니다.
-
<scope-end>는 <selector-list> 셀렉터로, 범위 한계를 식별합니다.
-
<rule-list>는 범위 지정 스타일 규칙을 나타냅니다.
의사 요소(pseudo-elements)는 범위 루트나 범위 한계가 될 수 없습니다. <scope-start> 및 <scope-end> 내에서 사용하면 무효입니다.
2.5.3. 범위 지정 스타일 규칙
범위 지정 스타일 규칙은 비범위 규칙과 아래와 같은 차이점이 있습니다:
-
셀렉터는 범위 안에 있음 요소만 매칭할 수 있습니다. (이 규칙은 대상에만 적용되며, 셀렉터의 나머지 부분은 제한 없이 매칭됩니다.)
-
프렐류드로 <relative-selector-list>를 허용합니다 (<selector-list>만 허용하는 것이 아님). 이러한 상대 셀렉터는 :scope를 기준으로 상대적입니다.
-
<relative-selector-list> 내의 셀렉터가 결합자로 시작하지 않지만 중첩 셀렉터 또는 :scope 셀렉터를 포함한다면, 상대 셀렉터가 아닌 것으로 해석됩니다(단, 대상은 여전히 범위 안에 있음 조건을 만족해야 매칭됨).
@scope ( #my-component) { p{ color : green; } :scope p{ color : green; } }
저자는 명시적 결합자를 추가하여 암시적 관계를 조정할 수 있습니다:
@scope ( #my-component) { > p{ color : green; } :scope > p{ color : green; } }
저자는 범위 루트를 타겟하거나 셀렉터 내에서 명시적으로 위치시킬 수 있으며, :scope 또는 &를 셀렉터에 포함시킬 수 있습니다:
@scope ( #my-component) { :scope{ border : thin solid; } &{ border : thin solid; } main :scope p{ color : green; } main & p{ color : green; } }
:scope 또는 & 셀렉터 모두 범위 루트를 참조할 수 있지만, 이 맥락에서는 서로 다른 의미를 갖습니다:
- 셀렉터 매칭의 차이점
-
:scope 셀렉터는 범위 루트 자체만 매칭하고, & 셀렉터는 <scope-start> 셀렉터 목록에 매칭되는 모든 요소를 매칭할 수 있습니다.
- 셀렉터 특이성의 차이점
-
:scope 셀렉터는 다른 의사 클래스와 동일한 특이성을 갖지만, & 셀렉터는 <scope-start> 내 가장 특이성이 높은 셀렉터와 동일한 특이성을 갖습니다.
2.5.4. 범위 루트와 한계 식별
@scope 규칙은 아래와 같이 하나 이상의 범위를 생성합니다:
- 범위 루트 찾기
-
<scope-start>에 매칭되는 각 요소에 대해, 해당 요소를 범위의 범위 루트로 사용하여 범위를 생성합니다. <scope-start>가 지정되지 않은 경우, 범위 루트는 해당 스타일시트 소유 노드의 부모 요소입니다. (해당 요소가 없고, 포함하는 노드 트리가 섀도우 트리라면, 범위 루트는 섀도우 호스트입니다. 그렇지 않으면, 범위 루트는 포함된 노드 트리의 루트입니다.) <scope-start>에서 :scope 또는 & 셀렉터는 바깥 컨텍스트에서 정의된 대로 해석됩니다.
- 범위 한계 찾기
-
범위 루트로 생성된 각 범위에 대해, 해당 범위 한계는 범위 안에 있음이며 <scope-end>에 매칭되는 모든 요소로 설정됩니다. :scope 및 &는 범위 지정 스타일 규칙과 동일하게 해석됩니다.
style
요소에서 <scope-start> 셀렉터를 생략함으로써 로컬 범위를 지정할 수 있습니다.
예시:
< div > < style > @ scope { p { color : red ; } } </ style > < p > this is red</ p > </ div > < p > not red</ p >
이는 아래와 동일합니다:
< div id = "foo" > < style > @ scope ( # foo ) { p { color : red ; } } </ style > < p > this is red</ p > </ div > < p > not red</ p >
/* .content는 :scope의 직접 자식일 때만 한계로 간주됨 */ @scope ( .media-object) to( :scope > .content) { ...}
범위 한계는 :scope를 사용하여 범위 루트 외부의 요소도 참조할 수 있습니다. 예시:
/* .content는 :scope가 .sidebar 내부에 있을 때만 한계로 간주됨 */ @scope ( .media-object) to( .sidebar :scope .content) { ...}
2.5.5. 범위 중첩
@scope 규칙은 중첩될 수 있습니다. 이 경우, 중첩 스타일 규칙과 마찬가지로, 내부 @scope의 프렐류드 셀렉터(그 범위를 정의하는 셀렉터)는 외부 셀렉터에 의해 범위가 지정됩니다.
참고: 결과적으로 scope에 대한 추가로 중첩된 scoped style rules는 외부 및 내부 @scope 규칙에 의해 실질적으로 제한되지만, scoping root는 가장 안쪽의 @scope에 의해 정의됩니다. scope proximity는 scoped style rule subject와 scoping root 간의 거리를 측정하기 때문에, @scope의 가장 안쪽만이 nested @scope rules의 scope proximity를 결정하는 데 중요합니다.
범위 근접성 계산이 범위 중첩에 영향을 받아야 할까요? [이슈 #10795]
@scope ( .parent-scope) { @scope ( :scope > .child-scope) to( :scope .limit) { :scope .content{ color : red; } } }
다음과 동일합니다:
@scope ( .parent-scope > .child-scope) to( .parent-scope > .child-scope .limit) { .parent-scope > .child-scope .content{ color : red; } }
전역 이름 정의 at-규칙 (@keyframes, @font-face, @layer 등)은 @scope 내부에 정의되어도 유효하지만, 이를 감싸는 @scope 규칙의 범위에는 적용되지 않으며 별도의 영향도 받지 않습니다. 다만, 해당 규칙에 포함된 스타일 규칙(예: @layer 내부)은 범위 지정됨입니다.
2.6. CSS가 아닌 표현 힌트의 우선순위
3. CSSOM
3.1.
CSSScopeRule
인터페이스
CSSScopeRule
인터페이스는 @scope 규칙을 나타냅니다:
[Exposed =Window ]interface :
CSSScopeRule CSSGroupingRule {readonly attribute CSSOMString ?;
start readonly attribute CSSOMString ?; };
end
start
타입CSSOMString
start
속성은 규칙의 <scope-start>를 (괄호 제외) 직렬화한 결과를 반환하며, <scope-start>가 없으면 null을 반환합니다.end
타입CSSOMString
end
속성은 규칙의 <scope-end>를 (괄호 제외) 직렬화한 결과를 반환하며, <scope-end>가 없으면 null을 반환합니다.
4. 변경 사항
이 부록은 참고용(informative)입니다.
4.1. 2023년 3월 21일 작업 초안 이후 변경점
2023년 3월 21일 작업 초안 이후 주요 변경점은 다음과 같습니다:
-
:scope 셀렉터는 호스트가 특징 없는 섀도우 호스트일 때 그 범위 루트 요소를 매칭할 수 있습니다. (이슈 9025)
-
<scope-start> 및 <scope-end> 셀렉터는 엄격하게 처리됩니다. (이슈 10042)
-
@scope 규칙이 <scope-start> 없이 작성되면 섀도우 호스트에 범위가 지정되며, 섀도우 루트에는 지정되지 않습니다. (이슈 9178)
-
범위 근접성이 단일 범위 루트와 범위 지정 스타일 규칙 대상 사이의 단계수로 계산됨을 명확히 했습니다(이슈 10795에서 추가 논의 예정).
-
강력 범위 근접성(strong scope proximity) 삭제됨. (이슈 6790)
-
범위 지정 자손 결합자(scoped descendant combinator) 삭제됨(보류). (이슈 8628)
-
CSSScopeRule
인터페이스 추가됨. (이슈 8626)
4.2. 2021년 12월 21일 첫 공개 작업 초안 이후 변경점
2021년 12월 21일 첫 공개 작업 초안 이후 주요 변경점은 다음과 같습니다:
-
<scope-start>가 선택 사항으로 변경되어 암시적 범위 가능해짐. (이슈 6606)
-
의사 요소(pseudo-elements)가 @scope 프렐류드에서 금지됨. (이슈 7382)
-
셀렉터 범위 표기법(selector scoping notation) 삭제됨. (이슈 7709)
4.3. 5단계 이후의 추가 사항
5단계 이후 추가된 기능은 다음과 같습니다:
-
범위 정의(<scope-start>와 <scope-end> 셀렉터 조합)
-
하한 경계 선택을 위한 in-scope(:in()) 의사 클래스 추가
-
@scope 규칙을 통한 범위 지정 스타일시트 생성
-
계단식에서 범위 근접성 정의 추가
4.4. 4단계 이후의 추가 사항
4단계 이후 추가된 기능은 다음과 같습니다:
-
계단식 레이어가 계단식 정렬 기준에 추가됨(스타일 속성도 계단식 정렬 기준의 별도 단계로 정의되어 올바르게 상호작용).
-
@layer 규칙을 통한 계단식 레이어 정의 도입
-
layer/layer() 옵션이 @import 정의에 추가됨
-
이전 레이어로 값을 되돌리는 revert-layer 키워드 도입
4.5. 3단계 이후의 추가 사항
3단계 이후 추가된 기능은 다음과 같습니다:
-
revert 키워드 도입(계단식 롤백)
-
supports() 문법 도입(@import 규칙에서 지원 조건)
-
CSS 계단식 4 § 3.1 속성 별칭 참고. CSS에서 레거시 문법 지원을 위한 두 가지 속성 별칭 메커니즘 정의됨.
4.6. 2단계 이후의 추가 사항
2단계 이후 추가된 기능은 다음과 같습니다:
감사의 글
David Baron, Tantek Çelik, Keith Grant, Giuseppe Gurgone, Theresa O’Connor, Florian Rivoal, Noam Rosenthal, Simon Sapin, Jen Simmons, Nicole Sullivan, Lea Verou, 그리고 Boris Zbarsky 가 이 명세에 기여했습니다.
5. 개인정보 보호 고려 사항
-
스타일 규칙 적용을 통해 표현된 사용자 환경설정 및 UA 기본값은 계단식 처리 과정에서 노출되며, 문서에 적용된 계산된 스타일을 통해 추론될 수 있습니다.
6. 보안 고려 사항
-
계단식 처리 과정은 동일 출처와 교차 출처 스타일시트 간의 구분을 하지 않으므로, 교차 출처 스타일시트의 내용도 문서에 적용된 계산된 스타일을 통해 추론될 수 있습니다.
-
@import 규칙은 교차 출처 스타일시트 로딩에 CORS 프로토콜을 적용하지 않으며, 이를 자유롭게 import 및 적용할 수 있도록 허용합니다.
-
@import 규칙은
Content-Type
메타데이터가 없는 리소스(또는 호스트 문서가 쿼크 모드일 때는 모든 동일 출처 파일)를text/css
로 간주하여, 임의의 파일이 페이지에 import되어 CSS로 해석될 수 있게 하며, 그 결과 민감한 데이터가 계산된 스타일을 통해 추론될 수 있습니다.