CSS 중첩

이제 가장 좋아하는 CSS 전처리기 기능 중 하나인 스타일 규칙 중첩이 언어에 내장되었습니다.

Adam Argyle
Adam Argyle

중첩하기 전에 모든 선택자는 서로 별도로 명시적으로 선언되어야 했습니다. 이로 인해 반복, 스타일시트 대량, 분산된 작성 환경이 발생합니다.

이전
.nesting {
  color: hotpink;
}

.nesting > .is {
  color: rebeccapurple;
}

.nesting > .is > .awesome {
  color: deeppink;
}

중첩 후에는 선택기를 계속 이어서 사용할 수 있으며 관련 스타일 규칙을 그룹화할 수 있습니다.

이후
.nesting {
  color: hotpink;

  > .is {
    color: rebeccapurple;

    > .awesome {
      color: deeppink;
    }
  }
}

브라우저에서 사용해 보기

중첩은 선택기를 반복할 필요가 없도록 하면서 관련 요소의 스타일 규칙을 함께 배치하여 개발자에게 도움이 됩니다. 또한 스타일이 타겟팅하는 HTML과 일치하는 데 도움이 될 수 있습니다. 이전 예의 .nesting 구성요소가 프로젝트에서 삭제된 경우 파일에서 관련 선택기 인스턴스를 검색하는 대신 전체 그룹을 삭제할 수 있습니다.

중첩은 다음과 같은 작업에 도움이 됩니다. - 구성 - 파일 크기 줄이기 - 리팩터링

중첩은 Chrome 112부터 사용할 수 있으며 Safari Technical Preview 162에서 사용해 볼 수도 있습니다.

CSS 중첩 시작하기

이 게시물의 나머지 부분에서는 선택사항을 시각화하는 데 도움이 되는 다음 데모 샌드박스를 사용합니다. 이 기본 상태에서는 아무것도 선택되지 않고 모든 항목이 표시됩니다. 다양한 도형과 크기를 선택하여 문법을 연습하고 작동하는 모습을 확인할 수 있습니다.

크고 작은 원, 삼각형, 정사각형으로 이루어진 다채로운 그리드

샌드박스 안에는 원, 삼각형, 정사각형이 있습니다. 일부는 소형, 중형, 대형입니다. 다른 색상은 파란색, 분홍색 또는 보라색입니다. 모두 .demo 요소 내에 있습니다. 다음은 타겟팅할 HTML 요소의 미리보기입니다.

<div class="demo">
  <div class="sm triangle pink"></div>
  <div class="sm triangle blue"></div>
  <div class="square blue"></div>
  <div class="sm square pink"></div>
  <div class="sm square blue"></div>
  <div class="circle pink"></div>
  …
</div>

중첩 예시

CSS 중첩을 사용하면 다른 선택자의 컨텍스트 내에서 요소의 스타일을 정의할 수 있습니다.

.parent {
  color: blue;

  .child {
    color: red;
  }
}

이 예에서 .child 클래스 선택기.parent 클래스 선택기 내에 중첩되어 있습니다. 즉, 중첩된 .child 선택기는 .parent 클래스가 있는 요소의 하위 요소인 요소에만 적용됩니다.

또는 & 기호를 사용하여 상위 클래스가 배치되어야 하는 위치를 명시적으로 나타낼 수 있습니다.

.parent {
  color: blue;

  & .child {
    color: red;
  }
}

이 두 예시는 기능적으로 동일하며, 이 도움말에서 더 고급 예시를 살펴보면 옵션이 있는 이유가 더 명확해집니다.

서클 선택

이 첫 번째 예에서는 데모 내의 원만 페이드 처리하고 흐리게 처리하는 스타일을 추가하는 작업을 수행합니다.

중첩 없이 오늘날 CSS는 다음과 같습니다.

.demo .circle {
  opacity: .25;
  filter: blur(25px);
}

중첩을 사용하는 경우 다음 두 가지 유효한 방법이 있습니다.

/* & is explicitly placed in front of .circle */
.demo {
  & .circle {
    opacity: .25;
    filter: blur(25px);
  }
}

또는

/* & + " " space is added for you */
.demo {
  .circle {
    opacity: .25;
    filter: blur(25px);
  }
}

결과: .circle 클래스가 있는 .demo 내의 모든 요소가 흐리게 처리되어 거의 보이지 않습니다.

도형의 다채로운 그리드에 더 이상 원이 없으며 배경에 매우 희미하게 표시됩니다.
데모 사용해 보기

삼각형과 정사각형 선택

이 작업을 수행하려면 중첩된 여러 요소(그룹 선택자라고도 함)를 선택해야 합니다.

중첩 없이 CSS를 사용하는 방법에는 두 가지가 있습니다.

.demo .triangle,
.demo .square {
  opacity: .25;
  filter: blur(25px);
}

또는 :is() 사용

/* grouped with :is() */
.demo :is(.triangle, .square) {
  opacity: .25;
  filter: blur(25px);
}

중첩을 사용하는 두 가지 유효한 방법은 다음과 같습니다.

.demo {
  & .triangle,
  & .square {
    opacity: .25;
    filter: blur(25px);
  }
}

또는

.demo {
  .triangle, .square {
    opacity: .25;
    filter: blur(25px);
  }
}

결과: .demo 내에 .circle 요소만 남습니다.

도형으로 이루어진 다채로운 그리드에 원만 남아 있고 다른 모든 도형은 거의 보이지 않습니다.
데모 사용해 보기

큰 삼각형과 원 선택

이 작업에는 복합 선택기가 필요합니다. 요소가 선택되려면 두 클래스가 모두 있어야 합니다.

중첩 없이 오늘날 CSS는 다음과 같습니다.

.demo .lg.triangle,
.demo .lg.square {
  opacity: .25;
  filter: blur(25px);
}

또는

.demo .lg:is(.triangle, .circle) {
  opacity: .25;
  filter: blur(25px);
}

중첩을 사용하는 두 가지 유효한 방법은 다음과 같습니다.

.demo {
  .lg.triangle,
  .lg.circle {
    opacity: .25;
    filter: blur(25px);
  }
}

또는

.demo {
  .lg {
    &.triangle,
    &.circle {
      opacity: .25;
      filter: blur(25px);
    }
  }
}

결과: 모든 큰 삼각형과 원이 .demo 내에 숨겨집니다.

컬러풀한 그리드에는 작고 중간 크기의 도형만 표시됩니다.
데모 사용해 보기
복합 선택기 및 중첩 사용에 관한 전문가 도움말

중첩된 선택기를 연결하는 방법을 명시적으로 보여주므로 & 기호가 유용합니다. 다음 예를 참고하세요.

.demo {
  .lg {
    .triangle,
    .circle {
      opacity: .25;
      filter: blur(25px);
    }
  }
}

중첩하는 유효한 방법이지만 결과가 예상되는 요소와 일치하지 않습니다. 그 이유는 .lg.triangle, .lg.circle의 원하는 결과를 함께 적용하도록 지정하는 &가 없으면 실제 결과가 .lg .triangle, .lg .circle(자손 선택기)가 되기 때문입니다.

분홍색 도형을 제외한 모든 도형 선택

이 작업에는 요소에 지정된 선택기가 없어야 하는 부정 기능 가상 클래스가 필요합니다.

중첩 없이 오늘날 CSS는 다음과 같습니다.

.demo :not(.pink) {
  opacity: .25;
  filter: blur(25px);
}

중첩을 사용하는 두 가지 유효한 방법은 다음과 같습니다.

.demo {
  :not(.pink) {
    opacity: .25;
    filter: blur(25px);
  }
}

또는

.demo {
  & :not(.pink) {
    opacity: .25;
    filter: blur(25px);
  }
}

결과: 분홍색이 아닌 모든 도형이 .demo 내에 숨겨집니다.

이제 다채로운 그리드가 흑백으로 바뀌고 분홍색 도형만 표시됩니다.
데모 사용해 보기
&를 통한 정밀도 및 유연성

:not() 선택기로 .demo를 타겟팅하려고 한다고 가정해 보겠습니다. &는 다음과 같은 경우에 필수입니다.

.demo {
  &:not() {
    ...
  }
}

이렇게 하면 .demo:not().demo:not()로 결합됩니다. 이전 예에서는 .demo :not()가 필요했습니다. 이 알림은 :hover 상호작용을 중첩하려는 경우 매우 중요합니다.

.demo {
  &:hover {
    /* .demo:hover */
  }

  :hover {
    /* .demo :hover */
  }
}

중첩 예시 더보기

중첩을 위한 CSS 사양에는 더 많은 예시가 포함되어 있습니다. 예를 통해 문법에 대해 자세히 알아보려면 유효한 예와 잘못된 예를 다양하게 살펴보세요.

다음 몇 가지 예에서는 CSS 중첩 기능을 간략하게 소개하여 이 기능이 제공하는 다양한 기능을 이해하는 데 도움이 됩니다.

@media 중첩

선택자와 스타일을 수정하는 미디어 쿼리 조건을 찾기 위해 스타일시트의 다른 영역으로 이동하는 것은 매우 방해가 될 수 있습니다. 컨텍스트 내에서 바로 조건을 중첩할 수 있는 기능을 사용하면 이러한 방해 요소가 사라집니다.

중첩된 미디어 쿼리가 현재 선택자 컨텍스트의 스타일만 수정하는 경우 편의를 위해 최소 구문을 사용할 수 있습니다.

.card {
  font-size: 1rem;

  @media (width >= 1024px) {
    font-size: 1.25rem;
  }
}

&를 명시적으로 사용할 수도 있습니다.

.card {
  font-size: 1rem;

  @media (width >= 1024px) {
    &.large {
      font-size: 1.25rem;
    }
  }
}

이 예에서는 &를 사용하여 확장된 문법을 보여주고 .large 카드를 타겟팅하여 추가 중첩 기능이 계속 작동하는 것을 보여줍니다.

@rules 중첩에 대해 자세히 알아보세요.

어디서나 중첩

지금까지의 모든 예시는 이전 컨텍스트를 계속하거나 추가했습니다. 필요한 경우 컨텍스트를 완전히 변경하거나 재정렬할 수 있습니다.

.card {
  .featured & {
    /* .featured .card */
  }
}

& 기호는 문자열이 아닌 선택기 객체에 대한 참조를 나타내며 중첩된 선택기의 아무 곳이나 배치할 수 있습니다. 여러 번 배치할 수도 있습니다.

.card {
  .featured & & & {
    /* .featured .card .card .card */
  }
}

이 예시는 약간 쓸모없어 보이지만 선택기 컨텍스트를 반복할 수 있는 것이 유용한 경우가 분명히 있습니다.

잘못된 중첩 예시

중첩 구문 시나리오 중에는 잘못된 것이 있으며, 프리프로세서에서 중첩을 사용해 왔다면 놀랄 수도 있습니다.

중첩 및 연결

많은 CSS 클래스 이름 지정 규칙은 중첩을 통해 선택자를 문자열처럼 연결하거나 추가할 수 있다고 가정합니다. 선택자가 문자열이 아닌 객체 참조이므로 CSS 중첩에서는 작동하지 않습니다.

.card {
  &--header {
    /* is not equal to ".card--header" */
  }
}

자세한 내용은 사양을 참고하세요.

까다로운 중첩 예시

선택기 목록 및 :is() 내에서 중첩

다음과 같이 중첩된 CSS 블록을 살펴보세요.

.one, #two {
  .three {
    /* some styles */
  }
}

선택자 목록으로 시작하여 계속 중첩되는 첫 번째 예입니다. 이전 예에서는 선택기 목록으로만 끝났습니다. 이 중첩 예시에는 잘못된 것이 없지만, 특히 ID 선택기가 포함된 선택자 목록 내에서 중첩하는 것과 관련하여 구현 세부정보가 약간 까다로울 수 있습니다.

중첩의 의도가 작동하려면 가장 안쪽 중첩이 아닌 모든 선택자 목록이 브라우저에 의해 :is()로 래핑됩니다. 이 래핑은 작성된 컨텍스트 내에서 선택기 목록의 그룹화를 유지합니다. 이 그룹화(:is(.one, #two))의 부작용은 괄호 안의 선택기 내에서 가장 높은 점수의 특이성을 채택한다는 점입니다. 이는 :is()가 항상 작동하는 방식이지만 중첩 문법을 사용할 때는 작성된 내용과 정확히 일치하지 않으므로 놀랄 수 있습니다. 요약하면 ID 및 선택기 목록으로 중첩하면 매우 높은 특이성 선택기가 생성될 수 있습니다.

어려운 예를 명확하게 요약하기 위해 이전 중첩 블록이 다음과 같이 문서에 적용됩니다.

:is(.one, #two) .three {
  /* some styles */
}

ID 선택기를 사용하는 선택자 목록 내에서 중첩할 때 주의하거나 린터에 경고하도록 지시하세요. 이 선택자 목록 내의 모든 중첩의 특이성이 높습니다.

중첩과 선언 혼합

다음과 같이 중첩된 CSS 블록을 살펴보세요.

.card {
  color: green;
  & { color: blue; }
  color: red;
}

.card 요소의 색상은 blue입니다.

혼합된 스타일 선언은 중첩이 발생하기 전에 작성된 것처럼 맨 위로 호이스팅됩니다. 자세한 내용은 사양을 참고하세요.

해결 방법이 있습니다. 다음은 &에 세 가지 색상 스타일을 래핑하여 작성자가 의도한 대로 계단식 순서를 유지합니다. .card 요소의 색상은 빨간색입니다.

.card {
  color: green;
  & { color: blue; }
  & { color: red; }
}

실제로 중첩된 후의 모든 스타일을 &로 래핑하는 것이 좋습니다.

.card {
  color: green;

  @media (prefers-color-scheme: dark) {
    color: lightgreen;
  }

  & {
    aspect-ratio: 4/3;
  }
}

기능 감지

CSS 중첩을 기능 감지하는 방법에는 두 가지가 있습니다. 중첩을 사용하거나 @supports를 사용하여 중첩 선택자 파싱 기능을 확인합니다.

브라우저에서 CSS 중첩을 지원하는지 묻는 Bramus의 Codepen 데모 스크린샷입니다. 이 질문 아래에는 지원을 나타내는 녹색 상자가 있습니다.

중첩 사용:

html {
  .has-nesting {
    display: block;
  }

  .no-nesting {
    display: none;
  }
}

@supports 사용:

@supports (selector(&)) {
  /* nesting parsing available */
}

제 동료인 Bramus님이 이 전략을 보여주는 멋진 Codepen을 작성했습니다.

Chrome DevTools로 디버깅

현재 DevTools의 중첩 지원은 최소 수준입니다. 현재 스타일이 예상대로 스타일 창에 표시되지만 중첩 및 전체 선택자 컨텍스트의 추적은 아직 지원되지 않습니다. 이를 투명하고 명확하게 만들기 위한 설계와 계획이 있습니다.

Chrome DevTools 중첩 문법의 스크린샷

Chrome 113에서는 CSS 중첩을 추가로 지원할 계획입니다. 조금만 더 기다려 주시기 바랍니다.

앞으로

CSS 중첩은 버전 1만 있습니다. 버전 2에서는 더 많은 문법 설탕과 더 적은 암기 규칙이 도입됩니다. 중첩 파싱이 제한되지 않거나 까다로운 순간이 없도록 하는 데 대한 수요가 많습니다.

중첩은 CSS 언어의 큰 개선사항입니다. CSS의 거의 모든 아키텍처 측면에 작성 관련 의미가 있습니다. 버전 2를 효과적으로 지정하려면 이러한 큰 영향을 깊이 탐구하고 이해해야 합니다.

마지막으로 @scope, 중첩, @layer를 모두 사용하는 데모를 확인해 보세요. 정말 흥미로운 소식입니다.

회색 배경에 밝은 카드가 있습니다. 카드에는 제목과 텍스트, 몇 가지 작업 버튼, 사이버 펑크 스타일 이미지가 있습니다.