CSS 독학 가이드 8 - 고급 선택자와 가상 요소
선택자를 제대로 쓰면 HTML이 깔끔해진다
HTML에 class를 너무 많이 붙이면 코드가 지저분해지죠. 고급 선택자를 잘 쓰면 HTML은 간결하게 유지하면서도 원하는 요소를 정확히 선택할 수 있어요.
저는 처음엔 모든 요소에 class를 붙였는데, 선택자를 제대로 배우고 나니 class가 절반으로 줄었습니다!
자손 선택자 vs 자식 선택자
자손 선택자 (Descendant Selector)
모든 자손을 선택해요. (자식, 손자, 증손자 모두)
div p {
color: red;
}
<div>
<p>빨강</p> <!-- 선택됨 -->
<section>
<p>빨강</p> <!-- 선택됨 (손자도 포함) -->
</section>
</div>
자식 선택자 (Child Selector)
직접적인 자식만 선택해요.
div > p {
color: red;
}
<div>
<p>빨강</p> <!-- 선택됨 -->
<section>
<p>검정</p> <!-- 선택 안 됨 (손자는 제외) -->
</section>
</div>
언제 뭘 쓸까?
- 일반적: 자손 선택자 (
) - 정확히 1단계만: 자식 선택자 (
>)
인접 선택자
인접 형제 선택자 (+)
바로 다음 형제만 선택해요.
h2 + p {
font-size: 1.2rem;
color: #666;
}
<h2>제목</h2>
<p>이 문단만 선택됨</p>
<p>이건 선택 안 됨</p>
용도: 제목 바로 다음 첫 문단을 강조할 때!
일반 형제 선택자 (~)
뒤따르는 모든 형제를 선택해요.
h2 ~ p {
color: blue;
}
<h2>제목</h2>
<p>파랑</p>
<div>...</div>
<p>파랑</p> <!-- h2 뒤의 모든 p -->
속성 선택자
HTML 속성 값으로 선택해요.
기본 속성 선택
/* href 속성이 있는 a 태그 */
a[href] {
color: blue;
}
/* target="_blank"인 링크 */
a[target="_blank"] {
color: red;
}
/* type="text"인 input */
input[type="text"] {
border: 1px solid #ccc;
}
부분 매칭
/* href가 "https"로 시작 */
a[href^="https"] {
color: green;
}
/* href가 ".pdf"로 끝 */
a[href$=".pdf"] {
color: red;
}
/* href에 "google"이 포함 */
a[href*="google"] {
color: blue;
}
/* class에 "btn"이라는 단어 포함 */
[class~="btn"] {
padding: 10px;
}
실전 예제:
/* 외부 링크에 아이콘 추가 */
a[href^="http"]::after {
content: " 🔗";
}
/* PDF 링크 스타일 */
a[href$=".pdf"]::before {
content: "📄 ";
}
가상 클래스 (Pseudo-classes)
특정 상태의 요소를 선택해요.
사용자 동작
/* 마우스 올렸을 때 */
a:hover {
color: red;
}
/* 클릭 중일 때 */
button:active {
transform: scale(0.95);
}
/* 포커스 되었을 때 */
input:focus {
border-color: blue;
outline: none;
}
/* 방문한 링크 */
a:visited {
color: purple;
}
구조 가상 클래스
/* 첫 번째 자식 */
li:first-child {
font-weight: bold;
}
/* 마지막 자식 */
li:last-child {
border-bottom: none;
}
/* n번째 자식 */
li:nth-child(2) {
color: red;
}
/* 홀수 번째 */
li:nth-child(odd) {
background: #f0f0f0;
}
/* 짝수 번째 */
li:nth-child(even) {
background: #ffffff;
}
/* 3의 배수 */
li:nth-child(3n) {
color: blue;
}
/* 3n+1 (1, 4, 7, 10...) */
li:nth-child(3n+1) {
font-weight: bold;
}
/* 뒤에서 첫 번째 */
li:nth-last-child(1) {
margin-bottom: 0;
}
실행 결과 (홀수/짝수 예제):
- 1. 첫 번째 항목 (홀수, 굵게)
- 2. 두 번째 항목 (짝수, 빨강)
- 3. 세 번째 항목 (홀수, 3의 배수)
- 4. 네 번째 항목 (짝수, 3n+1 굵게)
- 5. 마지막 항목 (홀수, 하단 여백 없음)
실전 예제: 테이블 줄무늬
tr:nth-child(odd) {
background: #f9f9f9;
}
tr:nth-child(even) {
background: #ffffff;
}
타입 가상 클래스
/* 같은 타입의 첫 번째 */
p:first-of-type {
font-size: 1.2rem;
}
/* 같은 타입의 마지막 */
p:last-of-type {
margin-bottom: 0;
}
/* 같은 타입의 n번째 */
p:nth-of-type(2) {
color: blue;
}
상태 가상 클래스
/* 체크된 체크박스/라디오 */
input:checked {
accent-color: blue;
}
input:checked + label {
font-weight: bold;
}
/* 비활성화 */
button:disabled {
opacity: 0.5;
cursor: not-allowed;
}
/* 필수 입력 */
input:required {
border-color: red;
}
/* 선택 가능 (optional) */
input:optional {
border-color: gray;
}
/* 유효한 입력 */
input:valid {
border-color: green;
}
/* 유효하지 않은 입력 */
input:invalid {
border-color: red;
}
가상 요소 (Pseudo-elements)
실제로 존재하지 않는 요소를 만들어요. :: 두 개 콜론 사용!
::before와 ::after
요소의 앞/뒤에 콘텐츠를 추가해요.
.quote::before {
content: '"';
font-size: 2rem;
color: #ccc;
}
.quote::after {
content: '"';
font-size: 2rem;
color: #ccc;
}
content 속성 필수! 없으면 표시 안 돼요.
실전 예제 1: 아이콘 추가
.new-badge::after {
content: "NEW";
background: red;
color: white;
padding: 2px 5px;
font-size: 0.7rem;
margin-left: 5px;
border-radius: 3px;
}
실전 예제 2: 말풍선 꼬리
.tooltip {
position: relative;
background: #333;
color: white;
padding: 10px;
border-radius: 5px;
}
.tooltip::after {
content: "";
position: absolute;
bottom: -10px;
left: 50%;
transform: translateX(-50%);
border-left: 10px solid transparent;
border-right: 10px solid transparent;
border-top: 10px solid #333;
}
실전 예제 3: 클리어픽스
.clearfix::after {
content: "";
display: table;
clear: both;
}
float 레이아웃에서 필수였던 기술이에요! (지금은 Flexbox 씀)
::first-letter (첫 글자)
p::first-letter {
font-size: 3rem;
font-weight: bold;
float: left;
margin-right: 10px;
line-height: 1;
}
신문 스타일 첫 글자 장식!
::first-line (첫 줄)
p::first-line {
font-weight: bold;
color: #333;
}
::selection (선택 영역)
::selection {
background: #ffeb3b;
color: #000;
}
텍스트 드래그했을 때 색상 변경!
:not() 가상 클래스
특정 조건을 제외해요.
/* 마지막 아이템 빼고 모두 margin-bottom */
li:not(:last-child) {
margin-bottom: 10px;
}
/* disabled 아닌 버튼 */
button:not(:disabled) {
cursor: pointer;
}
/* .active 클래스 없는 링크 */
a:not(.active) {
color: gray;
}
/* 여러 조건 */
p:not(.intro):not(.outro) {
color: #666;
}
:is() 가상 클래스 (최신)
여러 선택자를 묶어요.
/* 이전 방식 */
h1 span,
h2 span,
h3 span {
color: red;
}
/* :is() 사용 */
:is(h1, h2, h3) span {
color: red;
}
복잡한 선택자 간소화:
/* 이전 */
.header a:hover,
.sidebar a:hover,
.footer a:hover {
color: blue;
}
/* :is() 사용 */
:is(.header, .sidebar, .footer) a:hover {
color: blue;
}
:has() 가상 클래스 (부모 선택!)
특정 자식을 가진 부모를 선택해요. CSS의 혁명!
/* img를 포함한 article */
article:has(img) {
display: grid;
grid-template-columns: 2fr 1fr;
}
/* 체크된 input을 가진 label */
label:has(input:checked) {
font-weight: bold;
}
/* 자식이 없는 div */
div:not(:has(*)) {
display: none;
}
실전 예제: 동적 레이아웃
/* 이미지가 있으면 2열, 없으면 1열 */
.card {
display: grid;
}
.card:has(img) {
grid-template-columns: 200px 1fr;
}
2023년부터 주요 브라우저 지원!
실전 조합 예제
외부 링크에 자동 아이콘
a[href^="http"]:not([href*="mysite.com"])::after {
content: " ↗";
font-size: 0.8em;
color: #999;
}
폼 검증 표시
input:required:valid {
border-color: green;
}
input:required:invalid:not(:placeholder-shown) {
border-color: red;
}
input:required:valid::after {
content: "✓";
color: green;
}
카드 호버 효과
.card {
position: relative;
transition: transform 0.3s;
}
.card::before {
content: "";
position: absolute;
top: 0;
left: 0;
right: 0;
bottom: 0;
background: rgba(0,0,0,0.1);
opacity: 0;
transition: opacity 0.3s;
}
.card:hover {
transform: translateY(-5px);
}
.card:hover::before {
opacity: 1;
}
선택자 우선순위 복습
!important(비추천)- 인라인 스타일 (1000점)
- ID 선택자 (100점)
- 클래스, 속성, 가상 클래스 (10점)
- 태그, 가상 요소 (1점)
- 전체 선택자 (0점)
#header .nav li a:hover { }
/* 100 + 10 + 1 + 1 + 10 = 122점 */
.nav a:hover { }
/* 10 + 1 + 10 = 21점 */
점수가 높은 게 우선 적용!
다음 단계
고급 선택자를 마스터했으니 이제 CSS 변수와 최신 기능을 배울 차례예요. 코드 재사용성과 유지보수성을 극대화할 수 있습니다!
다음 글에서는 CSS 변수(Custom Properties), calc(), clamp() 등 최신 CSS 기능을 다룰 거예요.
실습 과제:
- nth-child로 테이블 줄무늬 만들기
- ::before와 ::after로 말풍선 만들기
- :has()로 이미지 유무에 따라 레이아웃 바꾸기
- 외부 링크에 자동 아이콘 추가하기
선택자를 잘 쓰면 HTML에 불필요한 class가 줄어들어요. 깔끔한 코드의 시작!
시리즈 네비게이션
- 이전글: CSS 독학 가이드 - 애니메이션과 전환 효과
- 현재글: CSS 독학 가이드 - 고급 선택자와 가상 요소
- 다음글: CSS 독학 가이드 - CSS 변수와 최신 기능