":has() 는 feature 추가가 아니었어. JavaScript 카테고리 전체 제거였어."
패턴 전환
20 년 동안 "안에 뭐 있는지 기반으로 부모 스타일링" 의 답은: 자식 watch 하고 부모에 class 토글하고 그 class 스타일링하는 JavaScript 쓰기. "유효할 때까지 submit 비활성" 인디케이터 가진 모든 폼이 이거 했음. "main 비었을 때 aside 숨김" 가진 모든 layout 이 이거 했음. 내부 링크 hover 되면 켜지는 모든 카드가 이거 했음.
:has() 가 그 다 obsolete 시킴.
고가치 패턴 다섯
1. 폼 전역 검증 시각화
Per-field 에러 처리 안 적고 폼에 문제 있다고 표시. 폼 자체가 하이라이트된 상태 받음:
2. 사이드바 / pane 적응
Main content 없을 때 사이드바 숨김, 또는 콘텐츠가 기준 충족할 때 보임:
3. 인터랙티브 카드 하이라이트
자식 링크나 버튼이 hover 또는 focus 될 때 카드 하이라이트, JavaScript 없이:
4. 자손 기반 조건 장식
안에 뭐 포함하는지 기반으로 element 에 marker 추가:
5. Slotted content 기반 layout variation
Featured 항목 있는지에 따라 적응하는 grid layout:
Sibling :has 패턴
:has() 가 descendant 만이 아니라 어떤 combinator 든 안에 쓸 수 있어. Sibling 인식 패턴:
li:has(+ li:hover)— hover 된 거 바로 앞 항목 (앞 항목 스타일링).li:has(~ li.active)— active 한 거 앞 항목들.label:has(+ input:invalid)— 인접 input 이 invalid 한 label.
퍼포먼스 우려 (그리고 대부분 moot 한 이유)
:has() 퍼포먼스에 대한 초기 회의가 실제: 순진한 구현이 모든 DOM 변화마다 모든 부모의 :has 매치 재평가. 모던 브라우저 (2023+) 가 selector 캐싱과 invalidation 추적으로 최적화. 전형 애플리케이션엔 :has() 퍼포먼스가 다른 selector 와 구분 불가. 매우 큰 DOM 의 복잡한 안쪽 selector 가진 깊이 중첩된 :has() 만 측정 가능한 오버헤드 — 거기서도 옳은 답이 :has() 피하기가 아니라 안쪽 selector 단순화.
:has() 가 안 하는 것
- DOM 에 없는 동적 콘텐츠 변화 watch 안 함. 자식의 intrinsic 상태가 JavaScript-only (React 상태, Web Component 내부) 면
:has()가 못 봄. 상태가 DOM 속성이나 CSS 클래스로 surface 되어야:has()가 쿼리. - Event handler 대체 안 함.
:has(:hover)가 CSS 상태; JS 이벤트 발사 안 함. 자식 hover 될 때 코드 돌려야 하면 여전히 event listener 필요. - Shadow DOM 경계 안 통과. 부모 안
:has()가 custom element 의 shadow tree 안을 못 봄. Parts/slot 사용 또는 속성으로 상태 노출.
:has() 가 대체 가능한지 의심. 폼 검증 스타일, layout 적응, hover 전파, 콘텐츠 인식 장식 — 대부분이 이제 순수 CSS. 코드 적게, 버그 적게, 렌더 빠르게.브라우저 지원
Chrome, Edge, Safari, Firefox 걸쳐 baseline 2023. :has() 가 2026 년 production 안전. Caniuse 가 확인 — polyfill 안 필요.
피파의 노트
.chat-row:has(.attachment) .badge { display: inline; }. Council UI 가 brain 안 선택될 때 React 상태 통해 picker dim 하곤 했음; 이제 .council-panel:has(.brain-select:invalid) .picker { opacity: 0.5; }. React 에 살던 UI 상태 layer 전체가 이제 CSS 에. Component 가 더 단순해지고, 상태가 sync 빠질 자리 적어지고, 렌더가 빨라졌어.