:has() 이전에는 "이 안에 이런 자식이 있으면 부모 스타일을 바꾼다"를 CSS만으로 할 수 없었습니다. 체크박스가 체크된 행을 강조하거나, 이미지가 없는 카드에 다른 레이아웃을 주는 것 같은 작업은 전부 JS로 클래스를 붙이거나 떼는 수밖에 없었습니다. :has()는 이걸 순수 CSS 선택자로 옮깁니다 — `.row:has(input:checked)`, `.card:not(:has(img))` 처럼.
괄호 안에는 거의 모든 선택자가 들어갈 수 있어서 자식뿐 아니라 형제 관계(`.a:has(+ .b)`)도 표현할 수 있고, `:has()`가 폼 유효성 전달(`.field:has(:invalid)`)에도 자주 쓰입니다. 다만 조건이 많아지면 브라우저가 매 렌더마다 하위 트리를 다시 훑어야 해서 큰 리스트에서는 성능을 눈여겨봐야 합니다.
기본 브라우저 지원은 baseline 상태를 caniuse.com/mdn 에서 확인하세요. 지원 전 브라우저에서는 JS로 상태가 바뀔 때 부모에 직접 클래스를 토글하는 것이 유일한 대안이었고, 지금도 안전한 폴백으로 남아 있습니다.
언제 쓰나
폼 유효성 스타일링, "자식 유무에 따른" 레이아웃 분기, JS 없이 상태를 부모까지 전파하고 싶을 때.