Logo

CSS의 :has() 가상 클래스 사용법

CSS에서 자식이나 후손 요소는 아주 쉽게 선택할 수 있지만, 부모나 조상 요소를 선택하는 것은 불가능한 일이 었습니다. 그래서 오랫동안 자바스크립트를 동원해서 이러한 문제를 해결하곤 했었죠.

하지만 CSS에 :has() 가상 클래스가 추가되면서 이것도 이제 옛말이 되었습니다. 웹 개발자들이 많이 기다렸던 만큼 2023년 State of JS 설문 조사 가장 많이 채택된 기능으로 뽑히고도 했었죠.

State of JS 2023 Awards

이번 포스팅에서는 비교적 최근에 CSS에 추가된 기능인 :has() 가상 클래스를 어떻게 사용하는지 알아보도록 하겠습니다.

본 포스팅은 CSS 선택자(selector)와 대한 기본적인 이해가 필요합니다. 관련해서는 제가 별도 포스팅에서 다루고 있으니 참고하세요.

기본 문법

CSS의 :has() 가상 클래스는 결합자(combinator)처럼 기본적으로 2개의 선택자를 필요로 합니다. 첫 번째 선택자로 선택된 HTML 요소의 안에 두 번째 선택자로 선택이 가능한 HTML 요소가 있다면 첫 번째 선택자로 선택된 요소에 선언한 스타일이 적용됩니다.

선택자1:has(선택자2) {
  /* 스타일 선언 */
}

네, 맞습니다. 두 번째 선택자가 아닌 첫 번째 선택자에 스타일에 적용됩니다. 이 점이 CSS의 결합자와의 가장 큰 차이점이며, 기존에 CSS로 할 수 없었던 부모나 조상 요소를 선택하는 것이 가능해진 결정적인 이유입니다.

예를 들어, 내부에 <a> 요소가 있는 <p> 요소를 선택하여 스타일하고 싶다면 다음과 같이 CSS를 작성할 수 있고요.

p:has(a) {
  /* 스타일 선언 */
}

비활성화 된 <input> 요소가 있는 <form> 요소를 선택하여 스타일하고 싶다면 싶다면 다음과 같이 CSS를 작성할 수 있습니다.

form:has(input:disabled) {
  /* 스타일 선언 */
}

조상 요소를 선택할 때 가장 많이 사용되지만, 엄밀히 얘기하면 :has() 가상 클래스의 활용 범위는 더 넓습니다. CSS 결합자에서 사용하는 >, +, ~ 기호를 잘 활용하면, 부모 요소, 하나의 이전 형제, 여러 이전 형제도 선택할 수 있습니다. 이 부분에 대해서는 뒤에서 좀 더 설명드리도록 하겠습니다.

CSS 결합자에 대해서는 별도 포스팅에서 자세히 다루고 있으니 참고하세요.

조상 요소 선택

CSS에서 후손 요소를 선택하여 스타일하는 것은 아주 간단한 일입니다. 두 개의 선택자를 공백 문자로 연결해주면 첫 번째 선택자 안에서 두 번째 선택자와 부합하는 요소를 선택됩니다.

예를 들어, 다음과 같은 중첩된 상자가 있다고 가정해보겠습니다.

<div class="out">
  <div class="in"></div>
</div>

out 클래스가 달린 요소 안에 있는 in 클래스가 달린 요소의 배경을 빨간색으로 칠하는 것은 쉽습니다.

.out .in {
  background: red;
}

그런데 반대로 in 클래스가 달린 요소의 밖에 있는 out 클래스가 달린 요소를 선택하여 스타일하려면 어떻게 해야할까요? 🤔

예전에 이런 작업은 자바스크립트가 없이는 거의 불가능했었는데요. 이제는 CSS :has() 가상 클래스가 있으니 쉽게 해결할 수 있습니다.

CSS :has() 가상 클래스를 통해서 in 클래스가 달린 요소의 밖에 있는 out 클래스가 달린 요소의 배경을 초록색으로 칠해보겠습니다.

.out:has(.in) {
  background: green;
}

만약에 in 클래스가 달린 요소의 직계 부모 요소를 상대로만 제한하고 싶다면 > 기호를 사용하면 됩니다.

.out:has(> .in) {
  background: green;
}

이전 요소 선택

CSS 결합자를 사용해서 다음 요소를 어렵지 않게 선택하여 스타일할 수 있습니다. 두 개의 선택자를 + 기호로 연결해주면 첫 번째 선택자 다음으로 나오는 요소가 두 번째 선택자와 부합하면 선택이 되죠.

예를 들어, 다음과 같은 중첩된 상자가 있다고 가정해보겠습니다.

<div class="left"></div>
<div class="right"></div>

left 클래스가 달린 요소 다음에 오는 right 클래스가 달린 요소의 배경을 빨간색으로 칠해보겠습니다.

.left + .right {
  background: red;
}

반대로 right 클래스가 달린 요소의 앞에 있는 left 클래스가 달린 요소를 선택하여 스타일하려면 어떻게 해야할까요? 🤔

마찬가지로 CSS :has() 가상 클래스를 이용하면 어렵지 않습니다.

right 클래스가 달린 요소의 앞에 있는 left 클래스가 달린 요소의 배경을 초록색으로 칠해보겠습니다.

.left:has(+ .right) {
  background: green;
}

+ 기호 대신에 ~ 기호를 사용하면 바로 앞 뿐만 아니라 앞에 있는 모든 형제 요소를 선택할 수 있습니다.

.left:has(~ .right) {
  background: green;
}

자식 상태에 따른 부모 스타일

:has() 가상 클래스의 기본적인 사용법을 배웠으니 이 번에는 좀 더 재미있는 예제를 보여드릴까요?

자식 요소의 상태에 따라서 부모 요소의 스타일을 변경해줘야 할 때 :has() 가상 클래스가 특히 유용하게 사용될 수 있는데요.

예를 들어, 3개의 색깔 버튼을 감싸고 있는 상자가 있다고 가정해보겠습니다.

<div>
  <button class="red">빨강</button>
  <button class="green">초록</button>
  <button class="blue">파랑</button>
</div>

각 버튼을 누르고 있을 때 상자의 색깔이 이애 따라 바뀌게 하려면 어떻게 해야할까요?

부모 요소인 상자에 스타일을 적용해야 하니 :has() 가상 클래스를 사용해야할텐데요. 바로 :active 가상 클래스를 사용해서 자식 요소인 각 버튼이 활성화된 상태를 감지하면 되겠죠? 이 선택자를 :has() 가상 클래스의 인자로 넘겨주면 됩니다!

div:has(.red:active) {
  background: red;
}

div:has(.green:active) {
  background: green;
}

div:has(.blue:active) {
  background: blue;
}

아래 웹 페이지에서 각 버튼을 누르고 계시면 상자의 배경색이 바뀌는 것을 보실 수 있으실 겁니다.

마치면서

지금까지 CSS에 추가된 :has() 가상 클래스 어떻게 사용하는지 실습을 통해서 함께 살펴보았습니다.

:has() 가상 클래스는 부모 요소나 이전 요소를 선택할 수 있게 해주는 아주 유용한 기능입니다. 대부분의 모던 브라우저에서 지원되는 기능이니 실무에서 잘 활용하실 수 있으셨으면 좋겠습니다.