BestOW's Blog

CSS :has() — 드디어 온 "부모 선택자"

20년 넘게 CSS에 없던 부모 선택자. :has()는 단순한 부모 선택을 넘어 상태 기반 스타일링의 판을 바꿉니다.

2분 읽기

CSS에서 “자식 상태에 따라 부모를 스타일링”하려면 오랫동안 자바스크립트가 필요했습니다. :has()가 이 20년 묵은 공백을 메웠고, 단순한 부모 선택자 이상입니다.

기본: 자식으로 부모를 선택

/* figcaption을 가진 figure에만 여백 */
figure:has(figcaption) {
  margin-block: 2rem;
}

/* 유효하지 않은 입력을 포함한 form을 강조 */
form:has(:invalid) {
  border: 1px solid var(--danger);
}

:has(X)는 “X를 후손으로 가진 요소”를 선택합니다. 즉 조건은 안쪽, 스타일 대상은 바깥쪽입니다.

부모 선택을 넘어 — 관계 선택자

:has()는 결합자와 함께 쓰면 형제·상태 조합까지 표현합니다.

/* 체크된 체크박스 바로 뒤의 라벨 */
input:checked + label {
  font-weight: 700;
}

/* 뒤에 이미지가 오는 제목의 아래 여백 제거 */
h2:has(+ img) {
  margin-bottom: 0;
}

/* 카드 안에 이미지가 있으면 그리드 레이아웃으로 */
.card:has(img) {
  display: grid;
  grid-template-columns: auto 1fr;
}

JS를 걷어내는 실전 패턴

“자식이 하나도 없을 때” 빈 상태

.list:not(:has(li)) {
  display: none;
} /* 항목 없으면 목록 숨김 */

폼 유효성에 따른 제출 버튼 — 예전엔 입력마다 이벤트를 달아 JS로 토글하던 것을, 상태 기반 CSS로 상당 부분 대체할 수 있습니다.

다크모드 없이도 문맥 스타일 — 특정 컴포넌트를 포함한 컨테이너만 다르게.

성능과 지원

  • 지원: 모든 최신 브라우저에서 사용 가능합니다. Firefox가 121(2023년 말)에 합류하며 Baseline에 올랐습니다. 아주 구형을 지원해야 하면 폴백을 고려하세요.
  • 성능: :has()는 브라우저가 잘 최적화하지만, *:has(...)처럼 대상이 지나치게 넓거나 복잡한 셀렉터를 남발하면 스타일 계산 비용이 커질 수 있습니다. 선택 대상을 좁게 유지하세요.

:has()는 “상태를 클래스로 승격시켜 JS로 토글”하던 코드의 상당수를 없앱니다. 자식/형제 상태로 스타일이 갈린다면 먼저 :has()로 표현되는지 생각해 보세요.

댓글

← 목록으로