체크박스 하나 켜졌다고 카드 전체를 바꾸려면, 자바스크립트가 부모에 클래스를 붙였다 뗐다 해야 했다 — 자식의 상태를 보고 부모를 고르는 CSS :has()의 시대

체크박스 하나 켜졌다고 카드 전체를 바꾸려면, 자바스크립트가 부모에 클래스를 붙였다 뗐다 해야 했다 — 자식의 상태를 보고 부모를 고르는 CSS :has()의 시대
자식 요소의 상태에 반응해 부모 요소가 바뀌는 CSS :has() 개념 일러스트

CSS는 늘 위에서 아래로만 흘렀다

지난 20년간 CSS의 규칙은 하나였다. 부모가 자식에게 스타일을 물려준다. 방향은 언제나 위에서 아래였다. 그래서 '자식의 상태를 보고 부모를 바꾸는' 일은 CSS가 할 수 없는 영역이었다. 체크박스 하나가 켜졌다고 그 체크박스를 감싼 카드 전체의 테두리를 바꾸려면, 개발자는 자바스크립트로 체크박스를 계속 감시하다가 부모 요소에 클래스를 붙였다 뗐다 해야 했다. 별것 아닌 효과 하나에 코드 한 뭉치가 따라붙었다.

자식을 보고 부모를 고르는 선택자, :has()

:has()는 이 방향을 뒤집는다. "이런 자식을 가진 부모를 골라라"라고 말할 수 있게 됐다. 예를 들어 .card:has(img)는 '이미지를 품은 카드'만 고르고, form:has(input:invalid)는 '잘못 입력된 칸이 하나라도 있는 폼'을 통째로 고른다. 자바스크립트가 하던 감시·조건 판단을 이제 CSS 한 줄이 대신한다.

작은 회사 웹사이트에서 특히 빛나는 곳

겉보기엔 개발자용 기능 같지만, 손님이 매일 마주하는 화면에서 그 차이가 드러난다.

  • 문의 폼: 필수 칸이 비면 form:has(:invalid)로 '보내기' 버튼을 자연스럽게 흐리게 만들 수 있다. 손님은 뭘 덜 채웠는지 화면 흐름만으로 안다.
  • 사진 유무에 따른 레이아웃: 사진이 있는 메뉴 카드와 없는 카드를 :has(img) 한 줄로 다르게 배치해, 사진이 빠진 자리가 어색하게 비지 않는다.
  • 선택에 반응하는 화면: 손님이 옵션을 고르면 :has(:checked)로 관련 영역을 강조하거나 펼쳐, 지금 무엇을 고른 상태인지 한눈에 보이게 한다.

형제 요소까지 손이 닿는다

:has()는 부모뿐 아니라 옆에 놓인 형제 요소에도 반응한다. h2:has(+ p)처럼 '바로 뒤에 문단이 따라오는 제목'만 골라 간격을 조절하거나, 특정 항목이 선택됐을 때 그 아래 안내문을 펼치는 일이 자바스크립트 없이 가능하다. 화면의 한 부분이 다른 부분의 상태에 반응하는, 살아있는 느낌을 코드 부담 없이 낼 수 있다.

이제는 안심하고 써도 되는 시점

과거엔 브라우저 지원이 발목을 잡았지만, 2023년 말부터 크롬·사파리·엣지·파이어폭스 최신 버전이 모두 :has()를 지원한다. 오래된 브라우저를 쓰는 손님을 위해서는, 효과가 빠져도 기본 화면은 멀쩡히 보이도록 '있으면 좋은 장식'에 먼저 적용하는 방식이 안전하다.

작은 코드가 만드는 큰 차이

웹사이트의 완성도는 대개 이런 디테일에서 갈린다. 화면이 손님의 행동에 자연스럽게 반응하느냐, 아니면 뻣뻣하게 멈춰 있느냐. :has()는 그 반응을 자바스크립트 뭉치 없이, 가볍고 안정적으로 만들어 준다. CYAN은 이런 최신 웹 표준을 실제 작은 회사 홈페이지에 녹여, 손님이 '어, 이 사이트 뭔가 잘 만들었네'라고 느끼는 순간을 코드 몇 줄로 설계한다.