CSS의 :has()는 요소가 특정 상대 선택자와 일치하는 요소를 포함하거나 관련될 때 그 요소 자체를 선택할 수 있게 합니다. 예를 들어 article:has(img)는 이미지가 있는 article을 선택합니다. 이 기능은 흔히 부모 선택자라고 불리지만, 정의상 특정 자손뿐 아니라 앞이나 뒤의 상대 선택자 관계도 표현합니다. 넓은 범위에 무심코 적용하기보다 대상 관계를 명확히 제한하는 편이 좋습니다.

.card:has(> img)가 직계 img 자식을 가진 카드를 선택하고, wrapper 안에 img가 있는 카드는 선택하지 않는 비교

1. 상대 선택자의 기준점

:has() 안의 인수는 상대 선택자 목록입니다. 기준 요소를 왼쪽에 두고 그 뒤의 관계를 확인하므로 section:has(> h2)는 바로 아래 자식으로 제목을 가진 section을 찾습니다. :has(.error)처럼 하위 어느 깊이에나 적용되는 조건과 달리, >를 쓰면 관계 범위를 직접 표현합니다. 이 선택 결과는 기준 요소에 적용되며, 조건을 만족시키는 자손 자체를 선택하는 것과는 다릅니다.

:has()는 여러 selector를 쉼표로 나열할 수 있어 서로 다른 조건 가운데 하나가 참일 때 매칭할 수 있습니다. 선택자 안에 복잡한 논리를 몰아넣기 전에 실제로 어떤 조상이 상태를 가져야 하는지 정합니다. 구체적인 기준과 자손 관계를 좁히면 스타일의 의도를 읽기 쉽고, 구성 변경에 따른 예상치 못한 매칭도 줄일 수 있습니다.

2. 폼과 카드 상태를 표현하는 예

폼에서 label:has(input:invalid)처럼 관련된 입력 상태를 기준으로 라벨 스타일을 고를 수 있습니다. 카드 안에 미디어가 있을 때만 레이아웃을 달리하거나, 체크된 항목이 있는 컨테이너에 상태 스타일을 적용하는 경우에도 쓸 수 있습니다. 이런 규칙은 DOM을 바꾸거나 JavaScript로 상태 클래스를 매번 동기화하지 않아도 된다는 장점이 있습니다. 다만 유효성 상태 자체는 HTML 폼 검증 규칙에 따르며, 시각적 스타일은 사용자가 오류를 이해하도록 하는 텍스트나 접근성 피드백을 대체하지 않습니다.

컴포넌트의 루트에서 :has() 조건을 쓰면 내부의 요소 상태와 외부 레이아웃을 연결할 수 있습니다. 예를 들어 card:has(.thumbnail)은 썸네일 존재에 따라 카드 표현을 선택할 수 있습니다. 조건이 우연히 다른 중첩 컴포넌트의 콘텐츠까지 포착하지 않게, 클래스나 직접 자식 결합자를 사용해 의도를 제한합니다.

3. 특이성과 성능을 점검하기

:has()의 특이성은 인수 목록에서 가장 높은 특이성을 가진 선택자의 특이성을 따릅니다. 따라서 복잡한 인수를 추가하면 겉보기보다 강한 규칙이 될 수 있습니다. CSS cascade layers 또는 낮은 특이성 기법과 함께 설계하더라도, 실제로 어떤 선언이 이기는지 개발자 도구에서 확인하는 습관이 유용합니다. :where()와의 조합은 특정 인수 특이성을 낮추는 데 쓸 수 있지만, 선택 대상 자체의 규칙과 계층 순서를 함께 검토해야 합니다.

브라우저 엔진은 선택자를 평가하지만, 하나의 구문만으로 고정된 성능 비용을 단정할 수는 없습니다. 아주 넓은 문서 범위에 복잡한 조건을 적용하면 유지보수와 디버깅이 어려워집니다. 실제 페이지에서 대상 요소 수와 DOM 변경에 따른 결과를 확인하고, 필요한 컨테이너에만 적용하십시오. 지원되지 않는 브라우저가 대상에 포함된다면 @supports selector(:has(*)) 등을 이용해 대체 스타일을 설계할 수 있습니다.

4. 전문가 관점: 사용할지 결정하는 기준

W3C Selectors 명세는 :has()를 관계형 의사 클래스와 상대 선택자 평가 규칙으로 정의합니다. MDN의 개발자 안내는 폼 상태나 콘텐츠 존재 여부를 표현하는 실용 예와 특이성 주의를 설명합니다. 이 자료가 보여주는 것은 DOM을 거꾸로 탐색하는 임의의 스크립트 기능이 아니라, CSS 선택자 매칭의 선언적 조건입니다. 선택자가 스타일만을 위한 것인지, 별도의 의미 상태나 동작이 필요한지도 구분해야 합니다.

적용 전에는 기준 요소, 상대 관계, 인수 특이성, 지원 환경을 점검합니다. 그다음 대표 DOM과 중첩 사례에서 스타일이 매칭되는지 확인하고, 접근성에 필요한 상태 설명이 별도로 남아 있는지 살핍니다. 간단한 관계 표현에 사용하면 상태 클래스의 중복을 줄일 수 있지만, 모든 DOM 관계를 하나의 복잡한 선택자로 표현할 필요는 없습니다.

출처