CSS :has() 셀렉터로 부모 요소 선택하기

문제

CSS에서 “자식 요소의 상태에 따라 부모 스타일을 바꾸고 싶다”는 요구가 있으면, 예전에는 무조건 JavaScript를 써야 했다.

// 체크박스가 체크되면 부모 카드에 클래스 추가
checkbox.addEventListener('change', (e) => {
  e.target.closest('.card').classList.toggle('selected');
});

이런 단순한 스타일 변경에 JS를 쓰는 게 항상 찜찜했다.

해결

:has() 셀렉터를 쓰면 CSS만으로 가능하다. 모든 모던 브라우저에서 지원한다.

/* 체크된 체크박스를 가진 카드 */
.card:has(input:checked) {
  border-color: #3b82f6;
  background-color: #eff6ff;
}

/* 이미지가 있는 글 목록 항목 */
.post-item:has(img) {
  grid-template-columns: 200px 1fr;
}

/* 빈 입력 필드를 가진 폼 그룹 */
.form-group:has(input:placeholder-shown) {
  opacity: 0.7;
}

좀 더 실용적인 예제도 있다.

/* 에러 메시지가 보이면 입력 필드 테두리 빨갛게 */
.field:has(.error-message:not(:empty)) input {
  border-color: #ef4444;
}

/* 비디오를 포함한 섹션은 패딩 없이 */
section:has(video) {
  padding: 0;
}

핵심 포인트

  • :has()는 CSS에서 부모/형제 요소를 조건부로 선택할 수 있는 셀렉터다
  • JavaScript 없이 순수 CSS로 상태 기반 스타일링이 가능해졌다
  • 모든 모던 브라우저에서 지원한다 (Chrome 105+, Firefox 121+, Safari 15.4+)
  • 과도하게 복잡한 :has() 체이닝은 성능에 영향줄 수 있으니 주의한다