CSS 중첩 모듈 레벨 1

W3C 작업 초안,

이 문서에 대한 자세한 정보
이 버전:
https://www.w3.org/TR/2026/WD-css-nesting-1-20260122/
최신 공개 버전:
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 편집기
테스트 스위트:
https://wpt.fyi/results/css/css-nesting/

개요

이 모듈은 한 스타일 규칙 안에 다른 스타일 규칙을 중첩할 수 있는 기능을 소개하며, 하위 규칙의 선택자는 상위 규칙의 선택자를 기준으로 합니다. 이는 CSS 스타일시트의 모듈화 및 유지 관리를 향상시킵니다.

CSS는 구조화된 문서(예: HTML 및 XML)의 렌더링을 화면, 인쇄 등에서 설명하는 언어입니다.

문서 상태

이 섹션에서는 문서가 발행될 당시의 상태를 설명합니다. 현재 W3C 출판물 목록과 이 기술 보고서의 최신 개정판은 W3C 표준 및 초안 색인에서 확인할 수 있습니다.

이 문서는 CSS 작업 그룹에 의해 작업 초안으로 발행되었으며, 권고 트랙을 사용했습니다. 작업 초안으로 발행되었다고 해서 W3C 및 회원들의 승인 의미는 아닙니다.

이 문서는 초안이며 언제든지 업데이트, 교체 또는 다른 문서로 대체될 수 있습니다. 진행 중인 문서 외에는 인용하는 것이 적절하지 않습니다.

피드백은 GitHub 이슈 등록(권장)를 통해 보내주십시오. 제목에 스펙 코드 “css-nesting”을 포함하여 “[css-nesting] …댓글 요약…”와 같이 작성하십시오. 모든 이슈와 댓글은 보관됩니다. 또는 피드백을 (보관) 공개 메일링 리스트 www-style@w3.org로 보낼 수 있습니다.

이 문서는 2025년 8월 18일 W3C 프로세스 문서의 적용을 받습니다.

이 문서는 W3C 특허 정책에 따라 운영되는 그룹에 의해 작성되었습니다. W3C는 그룹의 산출물과 관련된 특허 공개의 공개 목록을 유지하며, 해당 페이지에는 특허 공개 방법에 대한 지침도 포함되어 있습니다. 개별적으로 특허의 실질적 청구권이 있다고 판단되는 경우, W3C 특허 정책 섹션 6에 따라 정보를 공개해야 합니다.

1. 소개

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

이 모듈은 스타일 규칙을 다른 스타일 규칙 안에 중첩하는 것을 지원하는 내용을 설명하며, 내부 규칙의 선택자가 외부 규칙에 의해 매치된 요소들을 참조할 수 있게 합니다. 이 기능은 관련된 스타일들을 CSS 문서 내의 단일 구조로 집계할 수 있게 하여, 가독성과 유지보수성을 향상시킵니다.

테스트

중첩에 대한 일반 테스트


1.1. 모듈 상호작용

이 모듈은 [CSS21] 파서 모델을 확장하는 새로운 파서 규칙을 도입합니다. 또한 [SELECTORS-4] 모듈을 확장하는 선택자들을 도입합니다. 일부 IDL 및 알고리듬은 [CSSOM-1] 모듈에서 정의된 것들을 확장하거나 수정합니다.

1.2.

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

2. 해설

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

좀 더 간결하게 작성하고 싶은 CSS가 있다고 상상해 보세요.

.foo {
  color: green;
}
.foo .bar {
  font-size: 1.4rem;
}

중첩(Nesting)을 사용하면, 다음과 같이 작성할 수 있습니다:

.foo {
  color: green;
  .bar {
    font-size: 1.4rem;
  }
}

Sass나 다른 CSS 전처리기에서 스타일을 중첩해 본 적이 있다. 매우 익숙하게 느껴질 것입니다.

부모 스타일 규칙 안에 어떤 규칙이든 중첩할 수 있습니다:

main {
  div { ... }
  .bar { ... }
  #baz { ...}
  :has(p) { ... }
  ::backdrop { ... }
  [lang|="zh"] { ... }
  * { ... }
}

기본적으로 자식 규칙의 선택자는 부모 규칙과 후손 결합자(descendant combinator)로 연결되는 것으로 가정되지만, 중첩된 선택자를 임의의 결합자(combinator)로 시작하여 이를 변경할 수 있습니다:

main {
  + article { ... }
  > p { ... }
  ~ main { ... }
}

새로운 & 선택자는 부모 선택자에 의해 매치된 요소들을 명시적으로 참조할 수 있게 해주므로, 이전 예제들은 다음과 같이 작성될 수 있습니다:

main {
  & + article { ... }
  & > p { ... }
  & ~ main { ... }
}

그러나 중첩된 선택자 내에서 &를 다른 위치에 배치하여 부모와 자식 규칙 간의 다른 유형의 관계를 나타낼 수 있습니다. 예를 들어, 이 CSS:

ul {
  padding-left: 1em;
}
.component ul {
  padding-left: 0;
}

중첩을 사용하면 다음과 같이 다시 쓸 수 있습니다:

ul {
  padding-left: 1em;
  .component & {
    padding-left: 0;
  }
}

다시 말해, & 는 “중첩 선택자를 여기에 넣고 싶다”는 것을 표시하는 방법을 제공합니다.

선택자 사이에 공백을 원하지 않을 때도 유용합니다. 예를 들어:

a {
  color: blue;
  &:hover {
    color: lightblue;
  }
}

이 코드는 a:hover {와 동일한 결과를 냅니다. &가 없다면 a :hover {가 되어—a와 :hover 사이의 공백에 유의하세요—호버 링크에 스타일이 적용되지 않습니다.

여러 수준으로 중첩할 수 있습니다—이미 중첩된 CSS 안에 또 중첩된 CSS를 넣는 식으로—원하는 만큼 계층을 쌓을 수 있습니다. 중첩은 Container Queries, Supports Queries, Media Queries 및/또는 Cascade Layers와 자유롭게 혼합할 수 있습니다. (거의) 모든 것이 모든 것 안에 들어갈 수 있습니다.

3. 스타일 규칙 중첩

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

중첩 스타일 규칙은 일반 스타일 규칙과 정확히 같지만, 상대 선택자(relative selectors)를 사용할 수 있다는 점이 다릅니다. 상대 선택자는 암묵적으로 부모 규칙에 의해 매치된 요소들을 기준으로 합니다.

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

  a {
    color: blue;
  }
}

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

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

중첩 규칙은 또한 nesting selector를 사용하여 부모 규칙이 매치한 요소를 직접 참조할 수도 있고, 상대 선택자 문법을 사용하여 "descendant" 이외의 관계를 지정할 수도 있습니다.

.foo {
  color: red;

  &:hover {
    color: blue;
  }
}

/* equivalent to: */

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

  + .bar {
    color: blue;
  }
}

/* equivalent to: */

.foo { color: red; }
.foo + .bar { color: blue; }
테스트

3.1. 구문

이제 스타일 규칙의 내용은 기존의 선언들 외에도 중첩 스타일 규칙at-rule을 허용합니다.

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

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

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

상대 선택자를 포함한 중첩 규칙은 암묵적인 nesting selector의 특이성(specificity)을 포함합니다. 예를 들어, .foo { > .bar {...}}.foo { & > .bar {...}}는 내부 규칙에 대해 동일한 특이성을 가집니다.

일부 중첩을 전처리하는 CSS 생성 도구들은 선택자들을 문자열로 연결하여, 작성자가 중첩 수준을 가로질러 단일 간단한 선택자를 구축할 수 있게 합니다. 이는 선택자 자체에 내부적으로 반복이 많을 때 파일 전반의 반복을 줄이기 위해 BEM과 같은 계층적 이름 패턴과 함께 사용되기도 합니다.

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

.foo {
  color: blue;
  &Bar { color: red; }
}
/* In Sass, this is equivalent to
  .foo { color: blue; }
  .fooBar { color: red; }
*/

이는 CSS에서는 허용되지 않으며, 중첩은 문법 변환이 아니라 부모 선택자가 실제로 매치하는 요소들에 대해 매칭하는 것이기 때문입니다.

또한 선택자 &Bar는 애초에 CSS에서 유효하지 않습니다, 왜냐하면 Bar 부분은 타입 선택자(type selector)이기 때문에 복합 선택자에서 반드시 맨 앞에 와야 하기 때문입니다. (즉, Bar&로 작성되어야 합니다.) 다행히도 CSS 중첩과 전처리기 문법 간에는 충돌이 없습니다.

A selector is said to nesting selector를 포함한다 if, when it was 파싱될 때 어떤 종류의 선택자로든 파싱되었을 때, 값이 "&"(U+0026 AMPERSAND)인 <delim-token>이 나타난다면 그렇다고 합니다.

참고: 이 표현은 :is(:unknown(&), .bar)와 같은 경우를 포착하기 위해 명시적으로 서술되어 있습니다, 여기서 알려지지 않은 선택자(unknown selector)는(알려지지 않았기 때문에 그 인자가 선택자로 파싱되는 것이 의도되었는지 여부를 알 수 없습니다) 선택자 내부에서 유일하게 &를 포함하는 경우가 있을 수 있습니다. 그런 선택자는 최신 브라우저에서만 지원되는 완전히 유효한 선택자일 수 있고, 우리는 파싱이 관련 없는 버전 문제에 의존하기를 원치 않으므로, 그것을 여전히 nesting selector를 포함하는 것으로 취급합니다.

만약 <forgiving-selector-list>의 항목이 nesting selector를 포함하지만 유효하지 않다면, 그 항목은 버려지지 않고 정확히 그대로 보존됩니다. (이것은 선택자의 매칭 동작을 변경하지는 않습니다—유효하지 않은 선택자는 여전히 아무것도 매치하지 않습니다—단지 선택자의 직렬화에만 영향을 줍니다.)

테스트

앞의 단락은 & 자체를 Selectors로 이동시킬 때 Selectors로 옮겨져야 합니다; 여기서는 편의상 임시로 수정해 두었습니다.

3.2. 예제

/* & can be used on its own */
.foo {
  color: blue;
  & > .bar { color: red; }
  > .baz { color: green; }
}
/* equivalent to
  .foo { color: blue; }
  .foo > .bar { color: red; }
  .foo > .baz { color: green; }
*/


/* or in a compound selector,
   refining the parent’s selector */
.foo {
  color: blue;
  &.bar { color: red; }
}
/* equivalent to
  .foo { color: blue; }
  .foo.bar { color: red; }
*/

/* multiple selectors in the list are all
   relative to the parent */
.foo, .bar {
  color: blue;
  + .baz, &.qux { color: red; }
}
/* equivalent to
  .foo, .bar { color: blue; }
  :is(.foo, .bar) + .baz,
  :is(.foo, .bar).qux { color: red; }
*/

/* & can be used multiple times in a single selector */
.foo {
  color: blue;
  & .bar & .baz & .qux { color: red; }
}
/* equivalent to
  .foo { color: blue; }
  .foo .bar .foo .baz .foo .qux { color: red; }
*/

/* & doesn’t have to be at the beginning of the selector */

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

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

/* But if you use a relative selector,
  an initial & is implied automatically */

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

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

/* Somewhat silly, but & can be used all on its own, as well. */
.foo {
  color: blue;
  & { padding: 2ch; }
}
/* equivalent to
  .foo { color: blue; }
  .foo { padding: 2ch; }

  // or

  .foo {
    color: blue;
    padding: 2ch;
  }
*/

/* Again, silly, but can even be doubled up. */
.foo {
  color: blue;
  && { padding: 2ch; }
}
/* equivalent to
  .foo { color: blue; }
  .foo.foo { padding: 2ch; }
*/

/* The parent selector can be arbitrarily complicated */
.error, #404 {
  &:hover > .baz { color: red; }
}
/* equivalent to
  :is(.error, #404):hover > .baz { color: red; }
*/

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

/* As can the nested selector */
.foo {
  & :is(.bar, &.baz) { color: red; }
}
/* equivalent to
  .foo :is(.bar, .foo.baz) { color: red; }
*/

/* Multiple levels of nesting "stack up" the selectors */
figure {
  margin: 0;

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

    > p {
      font-size: .9rem;
    }
  }
}
/* equivalent to
  figure { margin: 0; }
  figure > figcaption { background: hsl(0 0% 0% / 50%); }
  figure > figcaption > p { font-size: .9rem; }
*/

/* Example usage with Cascade Layers */
@layer base {
  html {
    block-size: 100%;

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

/* Example nesting Cascade Layers */
@layer base {
  html {
    block-size: 100%;

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

/* Example usage with Scoping */
@scope (.card) to (> header) {
  :scope {
    inline-size: 40ch;
    aspect-ratio: 3/4;

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

/* Example nesting Scoping */
.card {
  inline-size: 40ch;
  aspect-ratio: 3/4;

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

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

3.3. 다른 At-규칙의 중첩

이 명세는 중첩 스타일 규칙 외에도, 스타일 규칙 안에 중첩 그룹 규칙을 허용합니다: 본문에 스타일 규칙들을 포함하는 모든 at-규칙은, 달리 명시되지 않는 한, 스타일 규칙 안에 중첩될 수 있습니다.

이와 같이 중첩될 때, 중첩 그룹 규칙 블록의 내용은 <block-contents>로 파싱되며, <rule-list>로 파싱되지 않습니다:

특히, 다음 규칙들은 중첩 그룹 규칙이 될 수 있습니다:
테스트

이러한 중첩 그룹 규칙의 의미와 동작은, 달리 명시되지 않는 한, 다른 점이 없습니다.

예를 들어, 다음과 같은 조건부 중첩은 유효합니다:
/* Properties can be directly used */
.foo {
  display: grid;

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

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

/* and also equivalent to the unnested: */
.foo { display: grid; }

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

/* Conditionals can be further nested */
.foo {
  display: grid;

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

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

/* equivalent to */
.foo { display: grid; }

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

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

/* Example nesting Cascade Layers */
html {
  @layer base {
    block-size: 100%;

    @layer support {
      & body {
        min-block-size: 100%;
      }
    }
  }
}

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

/* Example nesting Scoping */
.card {
  inline-size: 40ch;
  aspect-ratio: 3/4;

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

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

연속적으로 직접 중첩된 속성들의 연속은 자동으로 중첩 선언 규칙으로 래핑됩니다. (이것은 CSSOM에서 관찰할 수 있습니다.)

테스트

3.3.1. 중첩된 @scope 규칙

@scope 규칙이 중첩 그룹 규칙일 때, <scope-start> 선택자 안의 &는 가장 가까운 조상 스타일 규칙에 의해 매치된 요소들을 가리킵니다.

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

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

다음과 동등합니다:

.parent { color: blue; }
@scope (.parent > .scope) to (:where(:scope) .limit) {
  :where(:scope) .content {
    color: red;
  }
}
& 선택자는 @scope 규칙에서 :where(:scope)와 같이 동작합니다.

3.4. 중첩 규칙과 선언의 혼합

하나의 스타일 규칙이 선언들 및 중첩 스타일 규칙 또는 중첩 그룹 규칙을 동시에 포함할 때, 이 세 가지는 임의로 혼합될 수 있습니다. 규칙들 이후나 그 사이에 오는 선언들은 암묵적으로 중첩 선언 규칙으로 래핑되어, 다른 규칙들에 대한 순서를 보존합니다.

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

/* equivalent to */
article { color: green; }
:is(article) { color: blue; }
article { color: red; }

/* NOT equivalent to */
article { color: green; }
article { color: red; }
:is(article) { color: blue; }

출현 순서 결정을 위해, 중첩 스타일 규칙중첩 그룹 규칙은 부모 규칙보다 나중에 오는 것으로 간주됩니다.

예를 들어:
article {
  color: blue;
  & { color: red; }
}

두 선언은 동일한 특이성(0,0,1)을 가지지만, 중첩 규칙은 부모 규칙보다 나중에 오는 것으로 간주되므로, color: red 선언이 캐스케이드에서 이깁니다.

반면, 다음 예에서는:

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

:where() 의사클래스는 nesting selector의 특이성을 0으로 낮추므로, color: red 선언은 이제 특이성이 (0,0,0)이 되어, "출현 순서"가 고려되기 전에 color: blue 선언에 패배합니다.

참고: 선언과 중첩 규칙을 자유롭게 섞을 수는 있지만, 이후의 속성들이 소스 텍스트에 나타나지 않는 중첩 선언 규칙으로 자동 래핑되기 때문에, 읽기 어렵고 다소 혼란스러울 수 있습니다. 가독성을 위해, 작성자들은 중첩 규칙보다 먼저 스타일 규칙 내의 모든 속성을 먼저 배치하는 것이 권장됩니다. (이것은 또한 구형 사용자 에이전트에서 다소 더 잘 동작하는 경향이 있습니다: 파싱과 오류 복구 방식의 특성상, 중첩 규칙 후에 나타나는 속성들이 건너뛰어질 수 있습니다.)

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

중첩 스타일 규칙을 사용할 때, 작성자는 부모 규칙에 의해 매치된 요소들을 참조할 수 있어야 합니다; 결국 이것이 중첩의 전부입니다. 이를 위해 이 명세는 새로운 선택자인 중첩 선택자를 정의하며, & (U+0026 AMPERSAND)로 표기합니다.

중첩 스타일 규칙의 선택자에서 사용될 때, 중첩 선택자는 부모 규칙에 의해 매치된 요소들을 나타냅니다. 다른 어떤 문맥에서 사용될 때에는, 해당 문맥에서의 :scope과 동일한 요소들을 나타냅니다 (달리 정의되지 않는 한).

테스트
중첩 선택자는 부모 스타일 규칙의 선택자로 치환하고 이를 :is() 선택자로 감싸는 방식으로 디슈거(Desugar)할 수 있습니다. 예를 들어,
a, b {
  & c {

는 다음과 동일합니다

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

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

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

&.foo에 의해 매치된 요소들만 나타냅니다; 다시 말해, 이는 다음과 동등합니다:

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

이 제한을 완화하고 싶지만, :is()와 &가 같은 기반 메커니즘을 사용하도록 동시에 처리해야 합니다. (이슈: Issue 7433)

특이성(specificity)은 부모 스타일 규칙의 선택자 목록에 있는 복합 선택자들 중 가장 큰 특이성과 동일합니다 (:is()의 동작과 동일), 또는 그런 선택자 목록이 없으면 0입니다.

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

다음과 같은 DOM 구조에서

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

텍스트는 빨간색이 아니라 파란색이 됩니다. &의 특이성은 #a([1,0,0])와 b([0,0,1])의 더 큰 값인 [1,0,0]이고, 따라서 & c 선택자의 전체 특이성은 [1,0,1]이 되어 .foo c([0,1,1])보다 큽니다.

주목할 점은, 이것은 중첩을 수동으로 확장해서 비중첩 규칙으로 만들었을 때와는 다른 결과라는 것입니다. 수동으로 확장하면 color: blue 선언은 b c 선택자([0,0,2])에 의해 매치될 것이고, #a c([1,0,1])에 의해 매치되지는 않을 것입니다.

왜 특이성이 비중첩 규칙과 다른가?

중첩 선택자는 의도적으로 :is() 의사클래스와 같은 특이성 규칙을 사용합니다. :is()는 인수들 중 가장 큰 특이성을 사용하며, 실제로 어떤 선택자가 매치되었는지 추적하지 않습니다.

이것은 성능상의 이유로 필요합니다; 선택자가 여러 가능한 특이성을 가지는 경우, 얼마나 정확하게 매칭되었는지에 따라, 선택자 매칭이 훨씬 복잡하고 느려집니다.

하지만 그건 질문을 회피하는 셈이죠: 왜 우리는 &:is() 기준으로 정의할까요? Nesting과 유사한 기능의 일부 비브라우저 구현은 결코 :is()로 변환하지 않으며, 주로 :is()가 도입되기 이전에 만들어졌기 때문입니다. 대신, 그들은 직접 변환(desugar)합니다; 하지만 이것은 자체적으로 중대한 문제를 동반하며, 일부 (비교적 흔한) 경우에는 가능성의 기하급수적 폭발 때문에 엄청난 선택자가 우발적으로 생성될 수 있습니다.

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

여기서 세 수준의 중첩, 각 수준에 세 개의 선택자가 있으면 27개의 디슈거된 선택자가 생성됩니다. 선택자 목록을 더 늘리거나 중첩 수준을 더 추가하거나 규칙을 더 복잡하게 만들면, 비교적 작은 규칙이 수 메가바이트에 달하는 선택자로 확장될 수 있습니다.

일부 CSS 도구는 휴리스틱으로 일부 변형을 버리며 최악의 경우를 피하지만, 사용자 에이전트가 사용할 수 있는 선택지는 아닙니다.

:is()로 디슈거하면 이러한 문제를 완전히 제거할 수 있지만, 특이성이 약간 덜 유용해지는 대가를 치러야 하고, 이는 합리적인 절충으로 판단되었습니다.

중첩 선택자는 부모 규칙에 의해 매치된 경우, 피처리스(featureless) 요소들도 매치할 수 있습니다.

중첩 선택자복합 선택자 내에서 위치하는 것은 동작에 차이를 만들지 않습니다 (즉, &.foo.foo&는 동일한 요소를 매치합니다), 다만 기존 규칙인 타입 선택자가 존재한다면 복합 선택자에서 반드시 맨 앞에 위치해야 한다는 규칙은 계속 적용됩니다 (즉, &div는 불법이며 대신 div&로 작성해야 합니다).

테스트

5. 중첩 선언 규칙

약간 기술적인 이유로, 스타일 규칙의 내용 시작 부분에 나타나는 속성들과 다른 규칙들과 뒤섞여 나타나는 속성들을 구별할 수 있어야 합니다.

예를 들어, 다음 두 규칙에서:
.foo {
  color: red;
  @media (...) {...}
  background: blue;
}

우리는 color: redbackground: blue를 약간 다르게 취급해야 합니다. 특히 CSSOM에서, color: red는 스타일 규칙의 style 속성에 노출되는 반면, background: blue는 대신 cssRules 목록에 표시되어야 합니다.

이를 위해, CSS 파서는 이러한 속성들을 자동으로 특수한 자식 규칙으로 래핑합니다. 그러나 만약 이들을 스타일 규칙으로 & 선택자를 사용해 래핑한다면, 다소 불운한 동작이 발생할 수 있습니다:

예를 들어, 다음에서
.foo, .foo::before {
  color: red;
  & {
    background: blue;
  }
}

중첩된 규칙은 background 속성을 .foo::before 요소에 적용하지 않습니다, 왜냐하면 &가 의사요소를 표현할 수 없기 때문입니다.

마찬가지로, 중첩된 비-스타일 규칙의 자식 선언들은 어떤 식으로든 규칙들로 노출되어야 합니다. 이러한 규칙들(예: @media)은 원래 style 속성이 없었기 때문에 이러한 문제가 발생합니다.

이 모든 문제를 해결하기 위해, 대신 연속적으로 직접 중첩된 속성들을 중첩 선언 규칙(nested declarations rule)로 감쌉니다.

달리 명시되지 않는 한, 중첩 선언 규칙중첩 스타일 규칙이며, 다른 스타일 규칙과 동일하게 동작합니다. 그것은 부모 스타일 규칙과 정확히 동일한 요소들과 의사요소들을 매치하며, 동일한 특이성 동작을 가집니다. (이는 & 선택자를 가진 스타일 규칙과 유사하지만, 위에서 설명한 것처럼 약간 더 강력합니다.)

중첩 선언 규칙이 존재하는가?

원래 이 명세는 스타일 규칙 내의 모든 선언을 함께 그룹화하여, 원래 위치에서 앞으로 "이동"한 것처럼 동작하도록 했습니다. 또한 중첩 그룹 규칙 내의 원시 선언들을 암시적으로 스타일 규칙으로 래핑했으며, 이때 & 선택자를 사용했습니다.

우리가 중첩 선언 규칙으로 전환한 데에는 두 가지 주요 이유가 있습니다.

첫째, & {...} 규칙을 사용하여 선언을 암묵적으로 중첩 그룹 규칙(nested group rule)으로 감싸는 것은 동작을 변경하기도 했습니다. 이 주석 다음의 예제에서 보여지듯, 부모 스타일 규칙이 의사 요소(pseudo-elements)를 포함하는 경우 문제가 발생하며, 그렇지 않은 경우에도, 중첩 선언의 특이성(specificity) 동작을 잠재적으로 변경할 수 있습니다. 중첩 선언 규칙(nested declarations rule)로 전환하면 이러한 문제를 피할 수 있으며, 중첩된 @media/기타의 동작이 *비*중첩 @media/기타와 동일하게 됩니다.

둘째, 향후 CSS 기능(특히 믹스인)과 관련된 몇 가지 세부사항은, 중첩된 선언들이 자동으로 스타일 규칙의 앞부분으로 옮겨지면 올바르게 작동하지 않습니다. 우리는 다른 규칙들과의 상대적 순서를 유지해야 하며, 이를 CSSOM에서 표현 가능하게 만들려면 어딘가에 래핑되어야 합니다. 일반적인 & {...} 규칙을 사용하면 앞의 단락과 같은 문제가 발생하므로, 중첩 선언 규칙을 사용하면 부작용 없이 이를 처리할 수 있습니다.

예를 들어, 다음 스타일시트 스니펫에서:
.foo, .foo::before, .foo::after {
  color: black;
  @media (prefers-color-scheme: dark) {
    & {
      color: white;
    }
  }
}

다크 모드 페이지에서는 .foo 요소의 텍스트 색상이 흰색으로 변경되지만, ::before::after 의사요소들은 검은색으로 남아있습니다. 이는 & 선택자가 의사요소를 표현할 수 없기 때문입니다.

하지만 대신 다음과 같이 작성되었다면:

.foo, .foo::before, .foo::after {
  color: black;
  @media (prefers-color-scheme: dark) {
    color: white;
  }
}

그러면 color: white는 암시적으로 중첩 선언 규칙으로 래핑되고, 이는 부모 스타일 규칙과 정확히 동일하게 매치되므로, 요소와 그 의사요소들 모두 다크 모드에서 흰색 텍스트를 가지게 됩니다.

규칙들 사이에 끼어있는 선언들은 암시적으로 중첩 선언 규칙으로 래핑되어 별도의 스타일 규칙의 일부가 됩니다. 예를 들어, 다음 CSS가 주어지면:
.foo {
  color: black;
  @media (...) {...}
  background: silver;
}

만약 .foo 규칙의 CSSOM 객체를 검사하면, 그 style 속성에는 오직 하나의 선언만 포함되어 있을 것입니다: color: black 선언입니다.

background: silver 선언은 대신 암시적으로 생성된 중첩 선언 자식 규칙에서 발견될 것이며, 위치는 fooRule.cssRules[1].style입니다.

테스트

6. CSSOM

참고: [CSSOM-1] 은 이제 CSSStyleRule 이 자식 규칙을 가질 수 있다고 정의합니다.

중첩 스타일 규칙(relative selector)에서 상대 선택자를 직렬화할 때, 선택자는 절대화되어야 하며, 암묵적인 중첩 선택자가 삽입되어야 합니다.

예를 들어, 선택자 > .foo& > .foo 로 직렬화됩니다.
테스트

6.1. The CSSNestedDeclarations Interface

The CSSNestedDeclarations 인터페이스는 중첩 선언 규칙을 나타냅니다.

[Exposed=Window]
interface CSSNestedDeclarations : CSSRule {
  [SameObject, PutForwards=cssText] readonly attribute CSSStyleProperties style;
};
The style 속성은 규칙에 대해 CSSStyleProperties 객체를 반환해야 하며, 다음과 같은 속성들을 가집니다:
computed flag

설정되지 않음

readonly flag

설정되지 않음

declarations

규칙에 선언된 선언들로, 지정된 순서로 정렬됩니다.

parent CSS rule

this

owner node

Null

The CSSNestedDeclarations 규칙은 그 선언 블록이 직접 직렬화된 것처럼 직렬화됩니다.

테스트

참고: 이는 예를 들어 insertRule 등을 사용하여 생성할 수 있는 여러 인접한 중첩 선언 규칙이 직렬화되고 다시 파싱될 때 하나의 규칙으로 병합된다는 것을 의미합니다.

개인정보 고려사항

이 명세에 대해 보고된 새로운 개인정보 관련 고려사항은 없습니다.

보안 고려사항

이 명세에 대해 보고된 새로운 보안 관련 고려사항은 없습니다.

7. 변경사항

2023-02-14 부 Working Draft 이후의 주요 변경사항:

적합성(Conformance)

문서 관례

적합성 요구사항은 설명적 단언과 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"로 규범 텍스트와 구분되어 나타납니다.

참고, 이것은 정보성 주석입니다.

권고사항(advisements)은 특별한 주의를 환기시키기 위해 규범적 섹션으로 스타일링되며, <strong class="advisement">로 규범 텍스트와 구분됩니다. 예: 사용자 에이전트는 접근 가능한 대안을 제공해야 합니다.

테스트

이 명세서의 내용과 관련된 테스트는 이와 같은 "Tests" 블록에 문서화될 수 있으며, 이러한 블록은 비규범적입니다.


적합성 클래스

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

스타일시트(style sheet)
CSS 스타일시트.
렌더러(renderer)
스타일시트의 의미를 해석하고 문서를 렌더링하는 UA.
작성 도구(authoring tool)
스타일시트를 작성하는 UA.

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

렌더러는 스타일시트를 적절한 명세에 따라 해석하는 것 외에도, 이 명세에서 정의한 모든 기능을 올바르게 파싱하고 문서를 적절히 렌더링하여 지원해야 적합합니다. 다만 장치의 한계로 인해 문서를 올바르게 렌더링하지 못하는 것은 UA를 비적합으로 만들지 않습니다. (예: 흑백 모니터에서 색을 렌더링할 필요는 없습니다.)

작성 도구는 스타일시트를 생성할 때 일반 CSS 문법과 이 모듈의 각 기능의 개별 문법에 따라 문법적으로 올바른 스타일시트를 작성하고, 이 모듈에 설명된 스타일시트의 다른 모든 적합성 요구사항을 충족하면 이 명세에 적합합니다.

부분적 구현(Partial implementations)

작성자가 앞으로 호환 가능한 파싱 규칙을 이용해 폴백 값을 지정할 수 있도록, CSS 렌더러는 지원 수준이 없는 at-규칙, 속성, 속성 값, 키워드 및 기타 구문 구성 요소들을 무효로 처리(그리고 적절히 무시)해야 합니다. 특히, 사용자 에이전트는 지원하지 않는 컴포넌트 값들을 선택적으로 무시하고 하나의 다중값 속성 선언에서 지원되는 값만을 존중해서는 안 됩니다: 어떤 값이라도 유효하지 않다고 간주되면(지원되지 않는 값들은 유효하지 않아야 합니다), CSS는 전체 선언이 무시되도록 요구합니다.

불안정 및 독점 기능의 구현

향후 안정된 CSS 기능과의 충돌을 피하기 위해, CSSWG는 미래 대비 권장사항을 따라 불안정 기능 및 독점 확장을 구현할 것을 권장합니다.

실험적이지 않은 구현

명세가 Candidate Recommendation 단계에 도달하면 실험적이지 않은 구현이 가능해지며, 구현자는 명세에 따라 올바르게 구현되었음을 입증할 수 있는 CR 수준 기능에 대해 접두어가 없는 구현을 릴리스해야 합니다.

CSS의 상호운용성을 확립하고 유지하기 위해, CSS 작업 그룹은 실험적이지 않은 CSS 렌더러가 접두어 없는 구현을 릴리스하기 전에 구현 보고서(및 필요한 경우 해당 구현 보고서에 사용된 테스트케이스)를 W3C에 제출할 것을 요청합니다. W3C에 제출된 테스트케이스는 CSS 작업 그룹의 검토와 수정 대상이 됩니다.

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

색인(Index)

이 명세서에서 정의된 용어

참조에 의해 정의된 용어

참고문헌(References)

규범적 참조문헌

[CSS-CASCADE-4]
Elika Etemad; Tab Atkins Jr.. CSS Cascading and Inheritance Level 4. 13 January 2022. 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. 6 September 2024. WD. URL: https://www.w3.org/TR/css-cascade-6/
[CSS-COLOR-4]
Chris Lilley; Tab Atkins Jr.; Lea Verou. CSS Color Module Level 4. 24 April 2025. CRD. URL: https://www.w3.org/TR/css-color-4/
[CSS-CONDITIONAL-3]
Chris Lilley; David Baron; Elika Etemad. CSS Conditional Rules Module Level 3. 15 August 2024. CRD. URL: https://www.w3.org/TR/css-conditional-3/
[CSS-SYNTAX-3]
Tab Atkins Jr.; Simon Sapin. CSS Syntax Module Level 3. 24 December 2021. CRD. URL: https://www.w3.org/TR/css-syntax-3/
[CSS21]
Bert Bos; et al. Cascading Style Sheets Level 2 Revision 1 (CSS 2.1) Specification. 7 June 2011. REC. URL: https://www.w3.org/TR/CSS2/
[CSSOM-1]
Daniel Glazman; Emilio Cobos Álvarez. CSS Object Model (CSSOM). 26 August 2021. WD. URL: https://www.w3.org/TR/cssom-1/
[RFC2119]
S. Bradner. Key words for use in RFCs to Indicate Requirement Levels. March 1997. Best Current Practice. URL: https://datatracker.ietf.org/doc/html/rfc2119
[SELECTORS-4]
Elika Etemad; Tab Atkins Jr.. Selectors Level 4. 11 November 2022. 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-BACKGROUNDS-3]
Elika Etemad; Brad Kemper. CSS Backgrounds and Borders Module Level 3. 11 March 2024. CRD. URL: https://www.w3.org/TR/css-backgrounds-3/
[CSS-CASCADE-5]
Elika Etemad; Miriam Suzanne; Tab Atkins Jr.. CSS Cascading and Inheritance Level 5. 13 January 2022. CR. URL: https://www.w3.org/TR/css-cascade-5/
[CSS-CONDITIONAL-5]
Chris Lilley; et al. CSS Conditional Rules Module Level 5. 30 October 2025. WD. URL: https://www.w3.org/TR/css-conditional-5/
[CSS-PSEUDO-4]
Elika Etemad; Alan Stearns. CSS Pseudo-Elements Module Level 4. 27 June 2025. WD. URL: https://www.w3.org/TR/css-pseudo-4/
[CSS-UI-4]
Tab Atkins Jr.; Florian Rivoal. CSS Basic User Interface Module Level 4. 20 January 2026. WD. URL: https://www.w3.org/TR/css-ui-4/

IDL 색인

[Exposed=Window]
interface CSSNestedDeclarations : CSSRule {
  [SameObject, PutForwards=cssText] readonly attribute CSSStyleProperties style;
};

이슈 색인

앞의 단락은 & 자체를 Selectors로 이동시킬 때 Selectors로 옮겨져야 합니다; 편의상 여기서는 임시로 수정해 두었습니다.
이 제한을 완화하고 싶지만, :is()와 :is()& 를 동시에 처리해야 합니다, 이들은 의도적으로 동일한 기반 메커니즘을 사용하기 때문입니다. (Issue 7433)