웹사이트를 만들다 보면 이상하게 손이 많이 가는 순간이 있습니다. "필수 입력칸이 비어 있으면 그 칸을 감싼 박스를 빨갛게", "장바구니가 비면 안내 문구를, 상품이 하나라도 있으면 결제 버튼을" 같은 요구입니다. 화면을 보면 단순한데, 막상 구현하면 항상 자바스크립트가 따라붙습니다. 부모 요소가 자식의 상태를 알아야 하기 때문입니다.
20년 동안 CSS는 부모를 고르지 못했다
CSS 선택자는 늘 한 방향이었습니다. 부모 안의 자식, 형 다음의 동생처럼 아래로 또는 옆으로만 내려갈 수 있었죠. 정작 실무에서 자주 필요한 건 반대 방향입니다. "이 입력칸이 오류 상태일 때, 그 칸을 감싼 줄 전체를 강조하라" 같은 요구는 자식에서 부모로 거슬러 올라가야 합니다. 이 '부모 선택자'는 CSS가 등장한 이래 가장 오래된 숙원이었고, 그래서 우리는 늘 자바스크립트로 클래스를 붙였다 뗐다 하며 우회했습니다.
:has()는 '조건이 있는 부모'를 고른다
:has()는 "안에 ○○를 가진 요소"를 선택합니다. 예를 들어 .field:has(input:invalid) 는 '잘못 입력된 input을 품고 있는 .field'를 골라냅니다. 자식의 상태가 부모의 스타일을 결정하는, 정확히 그 동작입니다. 형제 결합자와 함께 쓰면 label:has(+ input:focus) 처럼 '바로 뒤 input에 포커스가 들어온 라벨'도 잡을 수 있어, 활용 범위가 단순한 부모 찾기를 훌쩍 넘어섭니다.
작은 회사 사이트에서 바로 쓰는 세 곳
거창한 기능이 아니라, 매일 마주치는 작은 화면들이 깔끔해집니다.
- 문의 폼 검증: 입력값이 잘못된 칸만 골라 테두리와 안내 문구를 보여줄 수 있어, 검증 스크립트의 절반이 사라집니다.
- 빈 상태와 채워진 상태: 목록이 비었을 때와 항목이 있을 때 컨테이너를 다르게 보이게 해, 별도 플래그 없이 화면이 스스로 반응합니다.
- 현재 메뉴 강조: 펼쳐진 하위 메뉴를 가진 상위 항목을 자동으로 강조해, 내비게이션 상태 관리 코드를 덜어냅니다.
지금 써도 되나 — 지원 현황과 안전한 적용
2023년 이후 크롬, 사파리, 엣지, 파이어폭스 최신 버전이 모두 :has()를 지원합니다. 사실상 현행 브라우저에서는 안심하고 쓸 수 있는 단계입니다. 다만 오래된 환경을 고려한다면, :has()는 '있으면 더 좋은' 강화 장식으로 얹는 것이 안전합니다. 즉 자바스크립트 검증이나 기본 스타일은 그대로 두고, :has()로 시각적 완성도를 한 겹 더하는 식입니다. 지원하지 않는 브라우저는 그 한 겹만 못 볼 뿐, 기능은 멀쩡히 돌아갑니다.
코드가 줄면 고장날 곳도 준다
:has()의 진짜 가치는 화려한 효과가 아니라 덜어냄에 있습니다. 상태를 추적하던 자바스크립트가 사라지면, 그만큼 버그가 숨을 곳도 사라집니다. 작은 회사 웹사이트일수록 이런 단순함이 곧 유지보수 비용과 안정성으로 돌아옵니다. CYAN은 이렇게 브라우저가 기본으로 제공하는 기능을 먼저 활용해, 가볍고 오래가는 웹사이트를 만드는 것을 원칙으로 삼고 있습니다.