CSS 유틸리티

[:has()] 자식 상태에 따라 부모를 스타일링하는 CSS 부모 선택자

입력이 맞으면 칸이 초록, 다 체크하면 카드가 완료 색, 이미지가 있으면 레이아웃이 바뀝니다. 전부 JS 없이 CSS :has() 한 줄로 처리해요.

CSS :has():user-invalid:valid:checkedForm validationBaseline 2024
라이브 데모
새 탭에서 열기
데모 불러오는 중…

제작 과정

한 줄 요약 - :has()는 부모 요소가 안에 든 자식의 상태(입력이 올바른지, 체크됐는지, 이미지가 있는지)를 보고 스스로 스타일을 바꾸는 CSS 선택자입니다. 예전엔 JS로 해야 했던 일을 CSS 한 줄이 대신해요.

이럴 때 필요해요

지금까지 CSS 선택자는 항상 '위에서 아래로'만 흘렀습니다. 부모는 자식을 고를 수 있어도, 자식의 상태(:valid / :checked 등)를 보고 부모를 바꿀 방법이 없었죠. 그래서 '이메일이 올바르면 칸 전체를 초록으로' 같은 요구는 onChange(값이 바뀔 때 호출되는 이벤트)를 듣고 부모에 클래스를 붙였다 뗐다 하는 JS로만 가능했습니다. :has()는 바로 그 부모-자식 방향을 뒤집어 줍니다.

어떻게 동작하나

  1. 부모에 .has-field:has(input:valid) 처럼 :has(자식 조건)을 붙이면, 안의 input이 그 조건일 때만 부모 규칙이 적용됩니다.

  2. 이메일 칸은 :valid면 초록, :user-invalid(사용자가 건드린 뒤 틀렸을 때만 켜짐)면 빨강이 되어, 빈 칸부터 빨갛게 뜨는 사고를 막습니다.

  3. 체크리스트는 :has(:checked)를 세 번 겹쳐 세 항목이 모두 체크된 경우만 '모두 완료' 색으로 바꿉니다 (개수 세기 없이 CSS만으로).

핵심은 이거예요

부모가 자식의 상태를 본다는 것. 이 한 가지만 이해하면 폼 검증 색, 체크 강조, :has(img) 레이아웃 전환이 전부 같은 원리입니다. JS 이벤트 리스너와 className 토글이 통째로 사라져요.

놓치기 쉬운 것

  • :valid 대신 :user-invalid를 쓰세요. :invalid는 사용자가 입력하기 전부터 켜져 있어 첫 화면이 빨갛게 떠 보입니다.

  • :has()는 비용이 있는 선택자라 너무 넓은 범위(예: body:has(...))에 남발하면 느려질 수 있어요. 필요한 부모에만 좁게 거세요.

이런 곳에 써요

  • 회원가입/결제 폼의 실시간 검증 색 (틀린 칸만 빨강, 통과한 칸은 초록)

  • 약관 동의 체크리스트, 할 일 목록의 '모두 완료' 강조, 이미지 유무에 따라 펼쳐지는 카드

소스 코드

· 데모 페이지에서 자동 추출
import type { Metadata } from "next";
import { HasSelectorDemo } from "./-components/HasSelectorDemo";

export const metadata: Metadata = {
  title: "CSS :has() 부모 선택자 (데모)",
  description:
    ":has()는 자식의 상태를 보고 부모의 스타일을 바꾸는 CSS 선택자입니다. :has(:valid) / :has(:checked) / :has(img)로 JS 없이 폼 검증 색·체크 강조·레이아웃 전환을 처리해요.",
  robots: { index: false, follow: false },
};

export default function Page() {
  return (
    <main className="flex min-h-[100dvh] items-center justify-center bg-white">
      <HasSelectorDemo />
    </main>
  );
}
55조회수

댓글