CSS 중첩 모듈

W3C 워킹 드래프트,

이 문서에 대한 추가 정보
이번 버전:
https://www.w3.org/TR/2023/WD-css-nesting-1-20230214/
최신 공개 버전:
https://www.w3.org/TR/css-nesting-1/
에디터스 드래프트:
https://drafts.csswg.org/css-nesting/
히스토리:
https://www.w3.org/standards/history/css-nesting-1
피드백:
CSSWG 이슈 저장소
명세 내 인라인
에디터:
Tab Atkins-Bittner (Google)
Adam Argyle (Google)
이 명세 편집 제안:
GitHub 에디터

요약

이 모듈은 한 스타일 규칙 안에 또 다른 스타일 규칙을 중첩시킬 수 있는 기능을 도입합니다. 자식 규칙의 선택자는 부모 규칙의 선택자를 기준으로 합니다. 이는 CSS 스타일시트의 모듈성 및 유지보수성을 높여줍니다.

CSS는 구조화된 문서(HTML, XML 등)의 렌더링을 화면, 종이 등에서 기술하기 위한 언어입니다.

이 문서의 상태

이 섹션은 문서가 출판된 시점의 상태를 설명합니다. 현재 W3C 발행 목록과 이 기술 보고서의 최신 수정본은 W3C 기술 보고서 색인 https://www.w3.org/TR/에서 확인할 수 있습니다.

이 문서는 CSS 워킹 그룹워킹 드래프트로, 권고 트랙을 이용하여 출판하였습니다. 워킹 드래프트로 출판된 것은 W3C 및 회원들의 보증을 의미하지 않습니다.

이 문서는 초안이며, 언제든지 업데이트, 교체 또는 폐기될 수 있습니다. 진행 중인 작업 외의 용도로 이 문서를 인용하는 것은 적절하지 않습니다.

피드백은 GitHub 이슈 등록(권장)을 통해 제출해 주세요. 제목에 명세 코드 “css-nesting”을 포함하여 다음과 같이 작성해 주세요: “[css-nesting] …코멘트 요약…”. 모든 이슈와 코멘트는 아카이브됩니다. 또는 피드백은 (아카이브됨) 공용 메일링 리스트 www-style@w3.org로 보낼 수 있습니다.

이 문서는 2021년 11월 2일 W3C 프로세스 문서에 따라 관리됩니다.

이 문서는 W3C 특허 정책 하에 운영되는 그룹이 작성하였습니다. W3C는 그룹의 산출물과 관련하여 공개된 공개 특허 목록을 유지하고 있습니다. 해당 페이지에는 특허 공개 방법도 안내되어 있습니다. 실제로 특허를 알고 있는 개인은 해당 특허가 필수 청구항을 포함한다고 생각할 경우, W3C 특허 정책 6항에 따라 정보를 공개해야 합니다.

1. 소개

이 섹션은 규범적이지 않습니다.

이 모듈은 스타일 규칙을 다른 스타일 규칙 안에 중첩하여, 내부 규칙의 선택자가 외부 규칙에 의해 매칭된 요소를 참조할 수 있도록 하는 기능을 설명합니다. 이 기능을 통해 관련 스타일을 CSS 문서 내에서 하나의 구조로 집계할 수 있어, 가독성과 유지보수성이 향상됩니다.

1.1. 모듈 상호작용

이 모듈은 [CSS21] 파서 모델을 확장하는 새로운 파서 규칙을 도입합니다. 또한 [SELECTORS4] 모듈을 확장하는 선택자를 도입합니다.

1.2.

이 명세는 새로운 속성이나 값을 정의하지 않습니다.

1.3. 동기

중간 정도로 복잡한 웹 페이지의 CSS는 관련된 콘텐츠를 스타일링하기 위해 많은 중복을 포함하는 경우가 많습니다. 예를 들어, 다음은 [CSS-COLOR-3] 모듈의 한 버전에 대한 CSS 마크업의 일부입니다:

table.colortable td {
  text-align:center;
}
table.colortable td.c {
  text-transform:uppercase;
}
table.colortable td:first-child, table.colortable td:first-child+td {
  border:1px solid black;
}
table.colortable th {
  text-align:center;
  background:black;
  color:white;
}

중첩을 사용하면 관련 스타일 규칙을 다음과 같이 그룹화할 수 있습니다:

table.colortable {
  & td {
    text-align:center;
    &.c { text-transform:uppercase }
    &:first-child, &:first-child + td { border:1px solid black }
  }
  & th {
    text-align:center;
    background:black;
    color:white;
  }
}

중복을 제거하는 것뿐만 아니라, 관련 규칙의 그룹화는 결과 CSS의 가독성과 유지보수성을 높여줍니다.

2. 스타일 규칙 중첩

스타일 규칙은 다른 스타일 규칙 안에 중첩될 수 있습니다. 이러한 중첩 스타일 규칙은 일반적인 스타일 규칙과 동일하게 동작하며—선택자를 통해 요소에 속성을 연결하지만—부모 규칙의 선택자 컨텍스트를 "상속"하여, 부모의 선택자를 반복하지 않고도 한 번 이상 더 확장할 수 있습니다.

중첩 스타일 규칙은 일반 스타일 규칙과 동일하지만, 선택자식별자 또는 함수 표기법으로 시작할 수 없습니다. 또한, 중첩 스타일 규칙상대 선택자를 사용할 수 있습니다.

즉, 다음과 같이 중첩된 스타일 규칙은:
.foo {
  color: red;

  .bar {
    color: blue;
  }
}

유효하며, 다음과 동일합니다:

.foo {
  color: red;
}
.foo .bar {
  color: blue;
}

중첩 규칙은 중첩 선택자를 사용하여 부모 규칙의 매칭된 요소를 직접 참조하거나, 상대 선택자 문법을 사용하여 "자손" 이외의 관계를 지정할 수 있습니다.

.foo {
  color: red;

  &:hover {
    color: blue;
  }
}

/* 다음과 동일합니다: */

.foo { color: red; }
.foo:hover { color: blue; }
.foo {
  color: red;

  + .bar {
    color: blue;
  }
}

/* 다음과 동일합니다: */

.foo { color: red; }
.foo + .bar { color: blue; }
하지만, 중첩된 선택자가 식별자(즉, 타입 선택자)로 시작하는 것은 유효하지 않습니다:
div {
  color: red;

  input {
    margin: 1em;
  }
}
/* "input"이 식별자이기 때문에 유효하지 않습니다. */

이러한 선택자도 작성할 수 있지만, 약간 다르게 표현해야 합니다:

div {
  color: red;

  & input { margin: 1em; }
  /* 유효, 더 이상 식별자로 시작하지 않음 */

  :is(input) { margin: 1em; }
  /* 유효, 콜론으로 시작하며
     이전 규칙과 동일합니다. */
}
중첩 규칙 선택자에 제한이 있는 이유는 무엇입니까?

스타일 규칙을 단순하게 다른 스타일 규칙 안에 중첩하는 것은, 불행히도, 모호합니다—선택자 문법이 선언 문법과 겹치기 때문에, 구현에서는 선언인지 스타일 규칙의 시작인지 알기 위해 무제한 선행 분석이 필요합니다.

예를 들어, 파서가 color:hover ...를 처음 본다면, 이것이 color 속성 (잘못된 값으로 설정...)인지, 아니면 <color> 요소에 대한 선택자인지 알 수 없습니다. 지원하는 속성을 기준으로 식별하는 것도 불가능합니다; 구현이 지원하는 속성이 바뀌면 파싱 결과도 바뀌게 되기 때문입니다.

중첩 스타일 규칙이 식별자로 시작하는 것을 금지하면 이 문제가 해결됩니다—모든 선언은 속성명을 나타내는 식별자로 시작하므로, 파서는 바로 선언인지 스타일 규칙인지 알 수 있습니다.

일부 브라우저가 아닌 중첩 규칙 구현에서는 이러한 요구 사항을 적용하지 않습니다. 대부분의 경우 결국 속성과 선택자를 구분할 수 있지만, 그렇게 하려면 파서에 무제한 선행 분석이 필요합니다; 즉, 파서는 어느 쪽으로 해석해야 하는지 알기 전까지 알 수 없는 양의 콘텐츠를 보관해야 할 수도 있습니다. CSS는 지금까지 파싱에 소량의, 알려진 양의 선행 분석만 요구해왔으며, 이를 통해 더 효율적인 파싱 알고리즘을 사용할 수 있습니다. 따라서 무제한 선행 분석은 일반적으로 브라우저 구현에서 받아들여지지 않습니다.

2.1. 문법

스타일 규칙의 내용은 이제 기존의 선언뿐만 아니라 중첩 스타일 규칙at-규칙도 허용합니다.

중첩 스타일 규칙은 비중첩 규칙과 다음과 같이 다릅니다:

중첩 스타일 규칙이 어떻게 파싱되는지에 대한 정확한 세부사항은 [CSS-SYNTAX-3]에서 정의되어 있습니다.

CSSWG는 현재 파싱 선행분석의 결과를 탐구하고 있으며, 이에 따라 허용되는 문법이 조정될 수 있습니다. [이슈 #7961]

잘못된 중첩 스타일 규칙은 그 내용과 함께 무시되지만, 부모 규칙을 무효화하지는 않습니다.

예를 들어, 다음과 같은 중첩은 유효합니다:
/* & 단독 사용 가능 */
.foo {
  color: blue;
  & > .bar { color: red; }
  > .baz { color: green; }
}
/* .foo { color: blue; }
  .foo > .bar { color: red; }
  .foo > .baz { color: green; }
*/


/* 합성 선택자 내에서 부모 선택자를 세분화함 */
.foo {
  color: blue;
  &.bar { color: red; }
}
/* .foo { color: blue; }
  .foo.bar { color: red; }
*/

/* 리스트의 여러 선택자는 모두 부모 기준 */
.foo, .bar {
  color: blue;
  + .baz, &.qux { color: red; }
}
/* .foo, .bar { color: blue; }
  :is(.foo, .bar) + .baz,
  :is(.foo, .bar).qux { color: red; }
*/

/* 한 선택자 내에서 & 여러 번 사용 가능 */
.foo {
  color: blue;
  & .bar & .baz & .qux { color: red; }
}
/* .foo { color: blue; }
  .foo .bar .foo .baz .foo .qux { color: red; }
*/

/* &는 선택자 앞에 있을 필요 없음 */

.foo {
  color: red;
  .parent & {
    color: blue;
  }
}
/* .foo { color: red; }
  .parent .foo { color: blue; }
*/

.foo {
  color: red;
  :not(&) {
    color: blue;
  }
}
/* .foo { color: red; }
  :not(.foo) { color: blue; }
*/

/* 상대 선택자를 사용하면, 초기에 &가 자동으로 암시됨 */

.foo {
  color: red;
  + .bar + & { color: blue; }
}

/* .foo { color: red; }
  .foo + .bar + .foo { color: blue; }
*/

/* 다소 특이하지만 & 단독 사용도 가능 */
.foo {
  color: blue;
  & { padding: 2ch; }
}
/* .foo { color: blue; }
  .foo { padding: 2ch; }
  // 또는
  .foo {
    color: blue;
    padding: 2ch;
  }
*/

/* 역시 특이하지만, &를 반복할 수도 있음 */
.foo {
  color: blue;
  && { padding: 2ch; }
}
/* .foo { color: blue; }
  .foo.foo { padding: 2ch; }
*/

/* 부모 선택자는 임의로 복잡할 수 있음 */
.error, #404 {
  &:hover > .baz { color: red; }
}
/* :is(.error, #404):hover > .baz { color: red; }
*/

.ancestor .el {
  .other-ancestor & { color: red; }
}
/* .other-ancestor :is(.ancestor .el) { color: red; }
*/

/* 중첩 선택자도 복잡할 수 있음 */
.foo {
  & :is(.bar, &.baz) { color: red; }
}
/* .foo :is(.bar, .foo.baz) { color: red; }
*/

/* 여러 단계의 중첩은 선택자를 "쌓아올림" */
figure {
  margin: 0;

  > figcaption {
    background: hsl(0 0% 0% / 50%);

    > p {
      font-size: .9rem;
    }
  }
}
/* figure { margin: 0; }
  figure > figcaption { background: hsl(0 0% 0% / 50%); }
  figure > figcaption > p { font-size: .9rem; }
*/
		
/* Cascade Layers 예시 */
@layer base {
  html {
    block-size: 100%;

    & body {
      min-block-size: 100%;
    }
  }
}
/* @layer base {
    html { block-size: 100%; }
    html body { min-block-size: 100%; }
  }
*/

/* Cascade Layers 중첩 예시 */
@layer base {
  html {
    block-size: 100%;

    @layer support {
      & body {
        min-block-size: 100%;
      }
    }
  }
}
/* @layer base {
    html { block-size: 100%; }
  }
  @layer base.support {
    html body { min-block-size: 100%; }
  }
*/

/* Scoping 예시 */
@scope (.card) to (> header) {
  :scope {
    inline-size: 40ch;
    aspect-ratio: 3/4;
				
    > header {
      border-block-end: 1px solid white;
    }
  }
}
/* @scope (.card) to (> header) {
    :scope { inline-size: 40ch; aspect-ratio: 3/4; }
    :scope > header { border-block-end: 1px solid white; }
  }
*/

/* Scoping 중첩 예시 */
.card {
  inline-size: 40ch;
  aspect-ratio: 3/4;

  @scope (&) to (> header > *) {
    :scope > header {
      border-block-end: 1px solid white;
    }
  }
}

/* .card { inline-size: 40ch; aspect-ratio: 3/4; }
  @scope (.card) to (> header > *) {
    :scope > header { border-block-end: 1px solid white; }
  }
*/

다음과 같은 경우는 유효하지 않습니다:

/* 선택자가 식별자로 시작 */
.foo {
  color: blue;
  div {
    color: red;
  }
}
중첩을 전처리하는 일부 CSS 생성 도구들은 선택자를 문자열로 이어붙여서, 작성자가 중첩 단계에서 단일 단순 선택자를 만들 수 있도록 합니다. 이는 BEM과 같은 계층적 이름 패턴과 함께 파일 내 반복을 줄이기 위해 사용되기도 하며, 선택자 자체에 내부적으로 많은 반복이 있을 때 자주 활용됩니다.

예를 들어, 한 컴포넌트가 .foo 클래스를 사용하고 중첩된 컴포넌트가 .fooBar를 사용할 때, 이를 Sass에서 다음과 같이 작성할 수 있습니다:

.foo {
        color: blue;
        &Bar { color: red; }
}
/* Sass에서는 다음과 동일함
   .foo { color: blue; }
   .fooBar { color: red; }
*/

하지만, 이런 문자열 기반 해석은 작성자가 중첩 규칙에서 타입 선택자를 추가하려는 경우와 모호해질 수 있습니다. 예를 들어 Bar는 HTML에서 유효한 커스텀 요소 이름이기도 합니다.

CSS는 이렇게 하지 않습니다: 중첩 선택자의 각 구성 요소를 원자적으로 해석하며, 문자열로 이어붙이지 않습니다:

.foo {
        color: blue;
        &Bar { color: red; }
}
/* CSS에서는 다음과 동일함
   .foo { color: blue; }
   Bar.foo { color: red; }
*/

2.2. 기타 At-규칙 중첩

중첩 스타일 규칙뿐만 아니라, 이 명세는 중첩 그룹 규칙스타일 규칙 안에 허용합니다: 본문에 스타일 규칙이 포함되는 모든 at-규칙은 스타일 규칙 안에 중첩될 수 있습니다.

이렇게 중첩될 때, 중첩 그룹 규칙의 내용은 <style-block>으로 파싱되며, <stylesheet>로 파싱되지 않습니다:

구체적으로, 다음 규칙들은 중첩 그룹 규칙으로 중첩될 수 있습니다:

이러한 중첩 그룹 규칙의 의미와 동작은 별도로 명시되지 않는 한 변경되지 않습니다.

예를 들어, 다음과 같은 조건부 중첩은 유효합니다:
/* 속성을 직접 사용할 수 있음 */
.foo {
  display: grid;

  @media (orientation: landscape) {
    grid-auto-flow: column;
  }
}
/* 동일하게 해석됨
  .foo {
    display: grid;
				@media (orientation: landscape) {
      & {
        grid-auto-flow: column;
      }
    }
  }
*/

/* 최종적으로 해석됨
  .foo { display: grid; }

  @media (orientation: landscape) {
    .foo {
      grid-auto-flow: column;
    }
  }
*/

/* 조건부는 추가로 중첩 가능 */
.foo {
  display: grid;

  @media (orientation: landscape) {
    grid-auto-flow: column;

    @media (min-width > 1024px) {
      max-inline-size: 1024px;
    }
  }
}

/* 동일하게 해석됨
  .foo { display: grid; }

  @media (orientation: landscape) {
    .foo {
      grid-auto-flow: column;
    }
  }

  @media (orientation: landscape) and (min-width > 1024px) {
    .foo {
      max-inline-size: 1024px;
    }
  }
*/

/* Cascade Layers 중첩 예시 */
html {
  @layer base {
    block-size: 100%;

    @layer support {
      & body {
        min-block-size: 100%;
      }
    }
  }
}
/* 동일하게 해석됨
  @layer base {
    html { block-size: 100%; }
  }
  @layer base.support {
    html body { min-block-size: 100%; }
  }
*/

/* Scoping 중첩 예시 */
.card {
  inline-size: 40ch;
  aspect-ratio: 3/4;

  @scope (&) {
    :scope {
      border: 1px solid white;
    }
  }
}

/* 동일하게 해석됨
  .card { inline-size: 40ch; aspect-ratio: 3/4; }
  @scope (.card) {
    :scope { border-block-end: 1px solid white; }
  }
*/

직접 중첩된 모든 속성은 순서대로 모아 중첩 스타일 규칙& 선택자에 중첩된 것처럼 처리되며, 모든 자식 규칙 앞에 위치합니다. 이것은 OM에서도 동일하게 적용됩니다. (childRules 속성은 실제로 이 중첩 스타일 규칙으로 시작하며, 직접 중첩된 모든 속성을 포함합니다.)

예를 들어, 앞선 예시:

.foo {
  display: grid;

  @media (orientation: landscape) {
    grid-auto-flow: column;
  }
}
/* 동일하게 해석됨
  .foo {
    display: grid;

    @media (orientation: landscape) {
      & {
        grid-auto-flow: column;
      }
    }
  }
*/

은 실제로 정확히 동일하며, 동일한 CSSOM 구조를 생성합니다. CSSMediaRule 객체는 CSSStyleRule 객체를 .childRules 속성에 하나만 가지며, grid-auto-flow 속성을 포함합니다.

참고: 이런 규칙의 직렬화는 원래 작성된 방식과 다를 수 있으며, 직렬화 시에는 직접 중첩된 속성이 전혀 나타나지 않습니다.

2.2.1. 중첩 @scope 규칙

@scope 규칙이 중첩 그룹 규칙인 경우, &<scope-start> 선택자에서 가장 가까운 상위 스타일 규칙에 의해 매칭된 요소를 참조합니다.

본문의 스타일 규칙과 <scope-end> 선택자에 대해, @scope 규칙은 상위 스타일 규칙처럼 동작하며, <scope-start> 선택자에 의해 매칭된 요소를 대상으로 합니다.

즉, 다음 코드는:
.parent {
  color: blue;

  @scope (& > .scope) to (& .limit) {
    & .content {
      color: red;
    }
  }

다음과 동일합니다:

.parent { color: blue; }
@scope (.parent > .scope) to (.parent > .scope .limit) {
  .parent > .scope .content {
    color: red;
  }
}

2.3. 중첩 규칙과 선언 혼합

스타일 규칙에 선언과 중첩 스타일 규칙 또는 중첩 조건부 그룹 규칙이 모두 포함된 경우, 세 가지는 임의로 혼합될 수 있습니다. 하지만, 선언과 다른 규칙의 상대적 순서는 어떤 방식으로도 보존되지 않습니다.

예를 들어, 다음 코드에서:
article {
  color: green;
  & { color: blue; }
  color: red;
}

/* 동일하게 해석됨 */
article {
  color: green;
  color: red;
  & { color: blue; }
}

등장 순서(Order Of Appearance)를 결정할 때, 중첩 스타일 규칙중첩 조건부 그룹 규칙은 부모 규칙 이후로 간주됩니다.

예시:
article {
  color: blue;
  & { color: red; }
}

두 선언 모두 specificity가 (0,0,1)로 같지만, 중첩 규칙은 부모 규칙 이후로 간주되므로, color: red 선언이 캐스케이드에서 우선합니다.

반면, 아래 예시에서는:

article {
  color: blue;
  :where(&) { color: red; }
}

:where() 의 의사클래스는 중첩 선택자의 specificity를 0으로 낮춰주므로, color: red 선언은 (0,0,0) specificity를 갖게 되어, 등장순서 고려 이전에 color: blue 선언에 패배하게 됩니다.

참고: 선언과 중첩 규칙을 자유롭게 섞을 수 있지만, 읽기 어렵고 다소 혼란스러울 수 있습니다. 모든 속성이 마치 모든 규칙보다 앞에 오는 것처럼 동작하기 때문입니다. 가독성을 위해 작성자는 스타일 규칙에서 속성을 먼저 작성하고, 그 다음에 중첩 규칙을 두는 것이 좋습니다. (이 방식은 오래된 사용자 에이전트에서도 더 잘 동작합니다; 파싱 및 오류 복구 방식 특성상, 중첩 규칙 이후에 나오는 속성은 건너뛰어질 수 있습니다.)

참고: 다른 규칙 유형과 마찬가지로, 중첩이 있는 스타일 규칙의 직렬화는 원래 작성된 방식과 달라질 수 있습니다. 특히, 직접 중첩된 속성은 모든 중첩 규칙보다 먼저 직렬화되므로, 속성을 먼저 작성하는 것이 또 하나의 이유가 됩니다.

3. 중첩 선택자: & 선택자

중첩 스타일 규칙을 사용할 때, 반드시 부모 규칙에 의해 매칭된 요소를 참조할 수 있어야 합니다. 즉, 중첩의 주요 목적입니다. 이를 위해, 이 명세는 새로운 선택자인 중첩 선택자를 정의하며, & (U+0026 AMPERSAND)로 작성합니다.

중첩 스타일 규칙의 선택자에서 사용할 때, 중첩 선택자는 부모 규칙에 의해 매칭된 요소를 나타냅니다. 다른 컨텍스트에서 사용할 때는, 해당 컨텍스트에서 :scope와 동일한 요소를 나타냅니다 (별도 정의가 없는 한).

중첩 선택자는 부모 스타일 규칙의 선택자를 :is() 선택자 안에 감싸서 대체할 수 있습니다. 예를 들어,
a, b {
  & c { color: blue; }
}

는 다음과 동일합니다:

:is(a, b) c { color: blue; }

중첩 선택자는 가상 요소(pseudo-element)를 나타낼 수 없습니다 (:is() 가상 클래스의 동작과 동일).

예를 들어, 아래 스타일 규칙에서:
.foo, .foo::before, .foo::after {
  color: red;

  &:hover { color: blue; }
}

&.foo에 매칭된 요소만 나타내며, 즉 다음과 동일합니다:

.foo, .foo::before, .foo::after {
  color: red;
}
.foo:hover {
  color: blue;
}

이 제한을 완화하고 싶지만, :is()& 모두에 대해 동시에 완화해야 합니다. 둘은 일부러 동일한 기반 메커니즘 위에 구축되었기 때문입니다. (Issue 7433)

specificity중첩 선택자에서 부모 스타일 규칙의 선택자 리스트 중 가장 큰 specificity와 동일합니다 (:is()와 동일).

예를 들어, 다음 스타일 규칙이 있을 때:
#a, b {
  & c { color: blue; }
}
.foo c { color: red; }

그리고 DOM 구조가 다음과 같을 때:

<b class=foo>
  <c>Blue text</c>
</b>

텍스트는 파란색이 됩니다. &의 specificity는 #a ([1,0,0])와 b ([0,0,1]) 중 더 큰 [1,0,0]이므로, 전체 & c 선택자는 [1,0,1] specificity를 가지며, .foo c ([0,1,1])보다 큽니다.

특히, 이는 중첩을 수동으로 비중첩 규칙으로 확장했을 때와 다른 결과입니다. 이 경우 color: blue 선언은 b c 선택자([0,0,2])로 매칭되기 때문입니다, #a c([1,0,1])가 아니라.

왜 non-nested 규칙과 specificity가 다릅니까?

중첩 선택자:is() 가상클래스와 동일한 specificity 규칙을 고의적으로 사용합니다. 즉, 매칭된 선택자가 무엇인지 추적하지 않고, 인자 중 가장 큰 specificity만 사용합니다.

이는 성능상의 이유로 필요합니다; 하나의 선택자가 매칭 방식에 따라 여러 specificity를 가질 수 있다면, 선택자 매칭이 훨씬 더 복잡하고 느려집니다.

그렇다면 왜 &:is() 기준으로 정의합니까? 브라우저가 아닌 일부 중첩 기능 구현은 :is()로 변환(desugar)하지 않으며, 주로 :is() 도입 이전에 만들어졌기 때문입니다. 대신 직접 변환하지만, 이 경우 심각한 문제를 야기할 수 있습니다. 일부 경우에는 매우 흔한 상황에서도 엄청나게 많은 선택자가 생성될 수 있습니다.

.a1, .a2, .a3 {
  .b1, .b3, .b3 {
    .c1, .c2, .c3 {
      ...;
    }
  }
}

/* 단순하게 변환하면 */
.a1 .b1 .c1,
.a1 .b1 .c2,
.a1 .b1 .c3,
.a1 .b2 .c1,
.a1 .b2 .c2,
.a1 .b2 .c3,
.a1 .b3 .c1,
.a1 .b3 .c2,
.a1 .b3 .c3,
.a2 .b1 .c1,
.a2 .b1 .c2,
.a2 .b1 .c3,
.a2 .b2 .c1,
.a2 .b2 .c2,
.a2 .b2 .c3,
.a2 .b3 .c1,
.a2 .b3 .c2,
.a2 .b3 .c3,
.a3 .b1 .c1,
.a3 .b1 .c2,
.a3 .b1 .c3,
.a3 .b2 .c1,
.a3 .b2 .c2,
.a3 .b2 .c3,
.a3 .b3 .c1,
.a3 .b3 .c2,
.a3 .b3 .c3 {...}

여기서, 세 단계의 중첩에 각 선택자 리스트에 세 개씩 있으면 27개의 변환된 선택자가 생성됩니다. 리스트에 더 많은 선택자를 추가하거나, 중첩 단계가 늘어나거나, 중첩 규칙이 더 복잡해지면, 작은 규칙이 수 메가바이트(혹은 훨씬 더 많은) 선택자로 확장될 수 있습니다.

일부 CSS 도구는 일부 변형을 휴리스틱으로 생략하여 출력량을 줄이고 아마도 올바른 결과를 내지만, UA에서는 이런 방식이 불가합니다.

:is()로 변환하면 이 문제를 완전히 제거할 수 있으며, specificity가 다소 덜 유용해지지만 합리적인 트레이드오프로 판단했습니다.

중첩 선택자복합 선택자 안 어디서든 사용할 수 있으며, 타입 선택자 앞에도 올 수 있어 복합 선택자의 기존 순서 제한을 위반할 수 있습니다.

예를 들어, &div는 유효한 중첩 선택자이며, "부모 규칙에 매칭된 요소 중 div 요소만"을 의미합니다.

div&로 써도 같은 의미지만, 중첩 스타일 규칙의 시작에 사용할 수 없고, 선택자의 다른 위치에서는 가능할 수 있습니다.

4. CSSOM

4.1. CSSStyleRule의 변경 사항

CSS 스타일 규칙은 중첩 규칙을 가질 수 있게 됩니다:

partial interface CSSStyleRule {
  [SameObject] readonly attribute CSSRuleList cssRules;
  unsigned long insertRule(CSSOMString rule, optional unsigned long index = 0);
  undefined deleteRule(unsigned long index);
};

cssRules 속성은 CSSRuleList 객체를 반환해야 하며, 이는 자식 CSS 규칙을 나타냅니다.

insertRule(rule, index) 메서드는 CSS 규칙 삽입rule에 대해 자식 CSS 규칙index 위치에 실행한 결과를 반환해야 합니다.

deleteRule(index) 메서드는 CSS 규칙 제거자식 CSS 규칙index 위치에서 실행해야 합니다.

참고: 중첩 규칙이 있는 CSSStyleRule의 직렬화는 [CSSOM]에서 이미 CSS 규칙 직렬화로 잘 정의되어 있습니다.

참고: 중첩 스타일 규칙의 선택자가 무엇으로 시작할 수 있는지에 대한 제한은 CSS 규칙 삽입 5단계의 "CSS에서 부과하는 제약"으로 간주됩니다 (중첩 스타일 규칙을 허용하는 모든 것에 대해, CSSStyleRule 자체뿐만 아니라).

selectorText를 설정할 때, CSSStyleRule중첩 스타일 규칙이고, 반환된 선택자 그룹이 식별자(ident) 또는 함수 토큰으로 시작하는 선택자로 시작하면, 아무 것도 하지 않고 반환합니다.

위 문단은 CSSOM 알고리즘에 인라인될 예정이며, 별도로 패치하지 않을 것입니다.

적합성

문서 관례

적합성 요건은 설명적 단언과 RFC 2119 용어 조합으로 표현됩니다. 규범적 부분에서 “MUST”, “MUST NOT”, “REQUIRED”, “SHALL”, “SHALL NOT”, “SHOULD”, “SHOULD NOT”, “RECOMMENDED”, “MAY”, “OPTIONAL” 등의 용어는 RFC 2119에서 설명된 대로 해석해야 합니다. 단, 가독성을 위해 이 단어들은 이 명세에서 모두 대문자로 표기되지는 않습니다.

이 명세의 모든 텍스트는 명시적으로 비규범적임을 표시한 섹션, 예시, 노트를 제외하면 규범적입니다. [RFC2119]

이 명세서에서 예시는 “예를 들어(for example)”라는 단어로 시작하거나, 다음과 같은 class="example"로 규범 텍스트와 구분됩니다:

이것은 정보 제공을 위한 예시입니다.

정보 제공 노트는 “Note”로 시작하며, class="note"로 규범 텍스트와 구분됩니다:

참고, 이것은 정보를 위한 노트입니다.

권고(advisement)는 특별한 주의를 환기시키기 위해 스타일링된 규범 섹션으로, <strong class="advisement">로 다른 규범 텍스트와 구분되며 다음과 같습니다: UA는 접근 가능한 대안을 반드시 제공해야 합니다.

적합성 클래스

이 명세에 대한 적합성은 세 가지 적합성 클래스에 대해 정의됩니다:

스타일 시트
CSS 스타일 시트.
렌더러
스타일 시트의 의미를 해석하고, 해당 스타일 시트를 적용한 문서를 렌더링하는 UA.
저작 도구
스타일 시트를 작성하는 UA.

스타일 시트가 이 명세에 적합하려면, 이 모듈에서 정의한 문법을 사용하는 모든 문장이 일반 CSS 문법 및 각 기능별 문법에 따라 유효해야 합니다.

렌더러가 이 명세에 적합하려면, 해당 명세에서 정의한 대로 스타일 시트를 해석하는 것 외에도, 이 명세에서 정의한 모든 기능을 올바르게 파싱하고 문서를 렌더링해야 합니다. 단, UA가 장치의 한계로 인해 문서를 올바르게 렌더링하지 못하는 경우 UA가 비적합하다고 볼 수 없습니다. (예: UA는 흑백 모니터에서 색상을 렌더링할 필요는 없습니다.)

저작 도구가 이 명세에 적합하려면, 이 모듈의 일반 CSS 문법과 각 기능별 문법에 따라 문법적으로 올바른 스타일 시트를 작성해야 하며, 이 모듈에서 설명하는 스타일 시트의 모든 다른 적합성 요건도 충족해야 합니다.

부분 구현

작성자가 미래 호환 파싱 규칙을 활용하여 폴백 값을 할당할 수 있도록, CSS 렌더러는 지원 가능한 수준이 없는 모든 at-규칙, 속성, 속성값, 키워드, 기타 문법 구조를 반드시 무효로 간주하고 (적절히 무시해야 합니다). 특히, UA는 지원하지 않는 구성 값만 선택적으로 무시하고, 다중 값 속성 선언에서 지원되는 값만 적용해서는 안 됩니다: 어떤 값이 무효(지원하지 않는 값이어야 하므로)로 간주되면, CSS는 전체 선언을 무시해야 합니다.

불안정 및 독점 기능 구현

향후 안정적인 CSS 기능과의 충돌을 피하기 위해, CSSWG는 베스트 프랙티스를 따를 것을 권장하며, 불안정 기능 및 독점 확장 구현 시 참고해야 합니다.

비실험적 구현

명세가 후보 권고(CR) 단계에 도달하면, 비실험적 구현이 가능해지며, 구현자는 명세에 따라 올바르게 구현되었음을 입증할 수 있는 모든 CR-level 기능에 대해 접두사 없는 구현을 배포해야 합니다.

CSS의 구현 간 상호운용성을 확보하고 유지하기 위해, CSS 워킹 그룹은 비실험적 CSS 렌더러가 어떤 CSS 기능에 대해 접두사 없는 구현을 배포하기 전 구현 보고서(필요하다면 해당 테스트케이스 포함)를 W3C에 제출할 것을 요청합니다. W3C에 제출된 테스트케이스는 CSS 워킹 그룹의 검토 및 수정 대상이 됩니다.

테스트케이스 및 구현 보고서 제출에 대한 추가 정보는 CSS 워킹 그룹 웹사이트 https://www.w3.org/Style/CSS/Test/에서 확인할 수 있습니다. 문의는 public-css-testsuite@w3.org 메일링 리스트로 보내시기 바랍니다.

색인

이 명세서에서 정의된 용어

참조로 정의된 용어

참고문헌

규범적 참고문헌

[CSS-CASCADE-4]
Elika Etemad; Tab Atkins Jr.. CSS Cascading and Inheritance Level 4. 2022년 1월 13일. CR. URL: https://www.w3.org/TR/css-cascade-4/
[CSS-CASCADE-6]
Elika Etemad; Miriam Suzanne; Tab Atkins Jr.. CSS Cascading and Inheritance Level 6. 2021년 12월 21일. WD. URL: https://www.w3.org/TR/css-cascade-6/
[CSS-COLOR-4]
Tab Atkins Jr.; Chris Lilley; Lea Verou. CSS Color Module Level 4. 2022년 11월 1일. CR. URL: https://www.w3.org/TR/css-color-4/
[CSS-NESTING-1]
Tab Atkins Jr.; Adam Argyle. CSS 중첩 모듈. 2021년 8월 31일. WD. URL: https://www.w3.org/TR/css-nesting-1/
[CSS-SYNTAX-3]
Tab Atkins Jr.; Simon Sapin. CSS Syntax Module Level 3. 2021년 12월 24일. CR. URL: https://www.w3.org/TR/css-syntax-3/
[CSS-VALUES-4]
Tab Atkins Jr.; Elika Etemad. CSS Values and Units Module Level 4. 2022년 10월 19일. WD. URL: https://www.w3.org/TR/css-values-4/
[CSS21]
Bert Bos; et al. Cascading Style Sheets Level 2 Revision 1 (CSS 2.1) Specification. 2011년 6월 7일. REC. URL: https://www.w3.org/TR/CSS21/
[CSSOM]
Daniel Glazman; Emilio Cobos Álvarez. CSS 오브젝트 모델 (CSSOM). 2021년 8월 26일. WD. URL: https://www.w3.org/TR/cssom-1/
[RFC2119]
S. Bradner. Key words for use in RFCs to Indicate Requirement Levels. 1997년 3월. Best Current Practice. URL: https://datatracker.ietf.org/doc/html/rfc2119
[SELECTORS4]
Elika Etemad; Tab Atkins Jr.. 선택자 레벨 4. 2022년 11월 11일. WD. URL: https://www.w3.org/TR/selectors-4/
[WEBIDL]
Edgar Chen; Timothy Gu. Web IDL Standard. Living Standard. URL: https://webidl.spec.whatwg.org/

참고 참고문헌

[CSS-CASCADE-5]
Elika Etemad; Miriam Suzanne; Tab Atkins Jr.. CSS Cascading and Inheritance Level 5. 2022년 1월 13일. CR. URL: https://www.w3.org/TR/css-cascade-5/
[CSS-COLOR-3]
Tantek Çelik; Chris Lilley; David Baron. CSS Color Module Level 3. 2022년 1월 18일. REC. URL: https://www.w3.org/TR/css-color-3/
[CSS-CONDITIONAL-3]
David Baron; Elika Etemad; Chris Lilley. CSS Conditional Rules Module Level 3. 2022년 1월 13일. CR. URL: https://www.w3.org/TR/css-conditional-3/
[CSS-CONTAIN-3]
Tab Atkins Jr.; Florian Rivoal; Miriam Suzanne. CSS Containment Module Level 3. 2022년 8월 18일. WD. URL: https://www.w3.org/TR/css-contain-3/
[CSS-GRID-2]
Tab Atkins Jr.; Elika Etemad; Rossen Atanassov. CSS Grid Layout Module Level 2. 2020년 12월 18일. CR. URL: https://www.w3.org/TR/css-grid-2/
[CSS-UI-4]
Florian Rivoal. CSS Basic User Interface Module Level 4. 2021년 3월 16일. WD. URL: https://www.w3.org/TR/css-ui-4/
[HTML]
Anne van Kesteren; et al. HTML 표준. Living Standard. URL: https://html.spec.whatwg.org/multipage/

IDL 색인

partial interface CSSStyleRule {
  [SameObject] readonly attribute CSSRuleList cssRules;
  unsigned long insertRule(CSSOMString rule, optional unsigned long index = 0);
  undefined deleteRule(unsigned long index);
};

이슈 색인

CSSWG는 현재 파싱 선행분석의 결과를 탐구하고 있으며, 이에 따라 허용되는 문법이 조정될 수 있습니다. [이슈 #7961]
이 제한을 완화하고 싶지만, :is()& 모두에 대해 동시에 완화해야 합니다. 둘은 일부러 동일한 기반 메커니즘 위에 구축되었기 때문입니다. (이슈 7433)
위 문단은 CSSOM 알고리즘에 인라인될 예정이며, 별도로 패치하지 않을 것입니다.