현록

CSS 가상 클래스와 가상 요소 차이

CSS의 :hover::before는 이름과 문법이 비슷하다.
하지만 하나는 요소의 상태를 고르고, 다른 하나는 요소의 특정 부분이나 생성된 박스를 고른다.

가상 클래스와 가상 요소를 구분하면 선택자를 읽는 순서도 분명해진다.

선택 대상의 차이

가상 클래스는 문서 트리만으로 표현하기 어렵거나 외부 상태에 따라 달라지는 요소를 선택한다.
마우스 포인터가 올라간 요소를 고르는 :hover, 키보드 포커스가 드러나야 하는 요소를 고르는 :focus-visible, 첫 번째 자식을 고르는 :first-child가 여기에 속한다.

가상 요소는 선택한 요소의 특정 부분이나 문서 트리에 없는 별도의 대상을 선택한다.
첫 줄을 고르는 ::first-line, 목록 표시를 고르는 ::marker, 내용 앞뒤에 박스를 만드는 ::before::after가 대표적이다.

둘 다 HTML에 class를 추가하지 않고 대상을 고를 수 있다.
일반 class와 id의 역할은 HTML id와 class 차이에서 따로 정리했다.

콜론 문법

가상 클래스는 콜론 하나로 시작한다.
가상 요소는 콜론 두 개로 시작한다.

a:hover {
  text-decoration-thickness: 2px;
}

p::first-line {
  font-weight: 700;
}

과거 CSS 문법과의 호환 때문에 브라우저는 :before, :after, :first-line, :first-letter의 단일 콜론 표기도 받아들인다.
새 코드에서는 역할을 바로 구분할 수 있도록 ::before처럼 이중 콜론을 쓰는 편이 좋다.

상태 선택

가상 클래스는 사용자의 입력 상태를 스타일에 반영할 때 자주 사용한다.

.button:hover {
  background: #e7f0ff;
}

.button:focus-visible {
  outline: 3px solid #0969da;
  outline-offset: 2px;
}

:hover만 사용하면 터치 기기나 키보드 사용자가 같은 변화를 확인하기 어렵다.
클릭 가능한 요소의 중요한 상태는 포커스 스타일도 함께 제공해야 한다.

구조 상태에도 같은 문법을 사용한다.
예를 들어 li:first-child는 목록의 첫 번째 li 요소 자체를 선택한다.

부분과 생성 콘텐츠

가상 요소는 HTML에 별도 요소를 추가하지 않고 시각적인 부분을 꾸밀 때 유용하다.

<p class="notice">저장되었습니다.</p>
.notice::before {
  display: inline-block;
  width: 0.5em;
  height: 0.5em;
  margin-right: 0.5em;
  border-radius: 50%;
  background: #1a7f37;
  content: '';
}

이 예시의 ::before는 의미를 추가하지 않는 장식용 점이다.
스크린 리더에 전달되어야 하는 성공 메시지는 HTML 텍스트에 남아 있다.

content로 넣은 텍스트는 보조 기술에서 일관되게 읽힌다고 보장할 수 없다.
가격, 상태, 버튼 이름처럼 이해에 필요한 정보는 가상 요소에만 두면 안 된다.

결합과 주의점

가상 클래스와 가상 요소는 한 선택자에서 함께 사용할 수 있다.

.link:hover::after,
.link:focus-visible::after {
  content: ' →';
}

이 선택자는 먼저 hover 또는 focus-visible 조건에 맞는 .link를 고른 다음 그 요소의 ::after를 꾸민다.
이처럼 가상 요소를 다른 단순 선택자 뒤에 두면 상태의 대상과 꾸밀 부분을 구분하기 쉽다.

::before::aftercontent의 계산값이 none이면 생성되지 않는다.
또한 <img>처럼 내용이 외부 리소스로 대체되는 요소에는 기대한 방식으로 적용되지 않는다.

명시도 계산에서도 둘은 다르다.
:hover 같은 일반 가상 클래스는 class 선택자 항목으로 계산되고, 가상 요소는 type 선택자 항목으로 계산된다.
다만 :where()의 명시도는 0이고 :is(), :not(), :has()는 인자 목록에서 가장 높은 명시도를 사용하므로 함수형 가상 클래스의 예외 규칙을 따로 확인해야 한다.

전역 box-sizing에서 일반 요소와 가상 요소를 함께 선택하는 이유는 CSS box-sizing content-box와 border-box 차이에서 이어서 볼 수 있다.

참고 자료

관련 포스트
CSS px em rem 차이 thumbnail
CSS px em rem 차이
CSS px, em, rem의 기준값과 중첩 계산 방식, 글꼴 크기와 컴포넌트 간격에 단위를 선택하는 기준을 정리합니다.
CSS display none과 visibility hidden 차이 thumbnail
CSS display none과 visibility hidden 차이
CSS display none과 visibility hidden의 차이를 레이아웃 공간, 포커스, 접근성, 자식 요소, opacity와의 차이로 정리합니다.
CSS position sticky와 fixed 차이 thumbnail
CSS position sticky와 fixed 차이
CSS position sticky와 fixed의 차이를 스크롤 기준, 문서 흐름, 부모 영역, top과 overflow, 고정 헤더 구현 기준으로 정리합니다.
CSS box-sizing content-box와 border-box 차이 thumbnail
CSS box-sizing content-box와 border-box 차이
CSS box-sizing의 content-box와 border-box 차이를 width 계산식, padding과 border, width 100% overflow, 전역 설정 방법으로 정리합니다.
CSS flex justify-content와 align-items 차이 thumbnail
CSS flex justify-content와 align-items 차이
CSS flex의 justify-content와 align-items 차이를 주축과 교차축, flex-direction, 가운데 정렬, 적용되지 않는 원인을 중심으로 정리합니다.
CSS margin과 padding 차이 thumbnail
CSS margin과 padding 차이
CSS margin과 padding의 차이를 박스 모델, 배경색과 클릭 영역, 전체 크기, margin collapsing, 축약형 작성 순서로 정리합니다.
CSS display block inline inline-block 차이 thumbnail
CSS display block inline inline-block 차이
CSS display의 block, inline, inline-block 차이를 줄바꿈, width와 height, margin 동작, 버튼과 태그 UI 선택 기준으로 정리합니다.
CSS position relative와 absolute 차이 thumbnail
CSS position relative와 absolute 차이
CSS position의 기본값, relative와 absolute의 배치 기준, normal flow 유지 여부, containing block, 부모에 position relative를 주는 이유를 초보자 기준으로 정리합니다.
CSS Flexbox와 Grid 차이 thumbnail
CSS Flexbox와 Grid 차이
CSS Flexbox와 Grid의 차이를 1차원과 2차원이라는 기준으로 정리합니다. 줄 바꿈된 Flexbox가 여전히 1차원인 이유와 목록, 카드, 혼합 레이아웃의 선택 기준도 함께 살펴봅니다.
CSS로 글래스모피즘 구현하기 thumbnail
CSS로 글래스모피즘 구현하기
안녕하세요. 오늘은 CSS로 글래스모피즘 디자인을 구현하는 법을 알아보겠습니다. 개인적으로 애플의 디자인 스타일을 좋아해서 좋은 레퍼런스 소재로 구경을 하는데, 오늘 문득 궁금해진 것이 CSS로 어떻게 글래스모피즘을 구현했느냐였습니다.