LOADING
Design
:has() 선택자 — 부모 요소를 자식 상태로 스타일링하기
2026.09.02· 1분 읽기· 1· 0
지금까지 CSS로 불가능했던 부모 선택자가 :has()로 가능해졌다. 실전에서 자주 쓰는 패턴 정리.
왜 필요했나
CSS는 오랫동안 자식의 상태에 따라 부모를 스타일링하는 방법이 없었다. 체크박스가 체크됐을 때 부모 label의 배경을 바꾸거나, 폼에 에러가 있는 input이 하나라도 있으면 fieldset 테두리를 빨갛게 만드는 것 — 전부 JS로 클래스를 토글해야 했다. :has()는 이걸 순수 CSS로 가능하게 한다.
기본 사용법
1/* 체크된 체크박스를 가진 label 스타일링 */ 2label:has(input:checked) { 3 background: var(--color-primary-soft); 4} 5 6/* 에러 input을 하나라도 포함한 fieldset */ 7fieldset:has(input:invalid) { 8 border-color: var(--color-danger); 9}
:has() 안의 선택자는 관계를 나타낸다 — label:has(input:checked)는 "체크된 input을 자손으로 가진 label"을 의미한다.
실전 패턴 1 — 이미지 유무에 따른 카드 레이아웃
1.card { 2 grid-template-columns: 1fr; 3} 4 5.card:has(img) { 6 grid-template-columns: 120px 1fr; 7}
이미지가 있는 카드만 2열 레이아웃으로 바뀐다. 예전 같으면 JS로 이미지 존재 여부를 확인해서 클래스를 붙여야 했다.
실전 패턴 2 — 형제 요소 상태에 반응하기
:has()는 +, ~ 결합자와 함께 쓰면 이전 형제를 선택할 수 있다 — 기존 CSS에는 없던 기능이다.
1/* 다음 형제가 :focus인 label */ 2label:has(+ input:focus) { 3 color: var(--color-primary); 4}
성능 관련 참고
:has()는 내부적으로 자손 트리를 검사해야 해서 일반 선택자보다 비용이 크다. 브라우저 벤더들이 최적화를 계속하고 있지만, 큰 리스트 안에서 반복되는 요소에 남용하면 렌더링 비용이 체감될 수 있다. 스코프를 좁혀서 쓰는 게 안전하다 — .list:has(...)보다 .list-item:has(...) 처럼 검사 범위를 작게 유지하는 식으로.
정리
:has()는 CSS에서 오래 요청됐던 부모 선택자 기능을 채워준다. 다만 만능은 아니고, 검사 범위가 넓어질수록 비용이 커진다는 점은 감안해야 한다.
0
이 글이 도움이 되셨나요?