CSS · 선택자와 우선순위
CSS? 는 "누구를 골라서 어떤 모양으로 바꿀까"를 적는 언어입니다. 여기서 "누구를 고르는 부분"이 선택자(selector) 예요. 그리고 한 요소에 서로 다른 규칙이 동시에 걸리면 누구 말을 들을지 정하는 규칙이 우선순위(명시도) 입니다. 이 페이지는 글로만 설명하지 않고, 칸마다 직접 고쳐서 바로 화면을 보는 라이브 예시를 함께 둡니다.
아래 회색 상자(라이브 데모)는 왼쪽에 진짜 HTML? + CSS 코드, 오른쪽에 그 결과 화면이 나옵니다. 코드를 직접 고치고 실행을 누르면 화면이 바뀌어요. 마우스를 올리거나(hover) 입력칸을 클릭(focus)해서 반응도 확인해 보세요. 읽기용으로만 적힌 코드는 흰 상자에 들어 있습니다.
1. 선택자 기본 — 타입·클래스·id·전체·그룹
무엇: 가장 기본이 되는 다섯 가지 "지목하는 법"입니다.
| 선택자 | 읽는 법 | 고르는 대상 |
|---|---|---|
p | 타입(요소) 선택자 | 모든 <p> 요소 |
.box | 클래스 선택자 | class="box" 가 붙은 모든 요소 |
#main | id 선택자 | id="main" 인 요소(페이지에 하나뿐이어야 함) |
* | 전체(universal) 선택자 | 모든 요소 |
h1, h2, p | 그룹 선택자(쉼표) | 나열한 것 모두에 같은 규칙 적용 |
p { color: gray; } /* 모든 문단 */
.box { padding: 10px; } /* class="box" 인 것 */
#main { border: 1px solid; }/* id="main" 인 것 */
* { margin: 0; } /* 모든 요소 */
h1, h2 { font-weight: 700; } /* h1 과 h2 둘 다 */실제로 그려지는 모습을 봅시다. 코드를 고쳐 색이나 글자를 바꿔 보세요.
같은 id 값은 한 페이지에 하나만 있어야 합니다. 여러 요소를 같은 모양으로 만들고 싶으면 id 가 아니라 클래스를 쓰세요. 클래스는 여러 요소에 마음껏 붙일 수 있습니다.
2. 결합자 — 관계로 고르기
무엇: "어떤 것 안에 있는", "바로 다음에 오는" 같은 위치 관계로 대상을 좁힙니다.
| 표기 | 이름 | 고르는 대상 |
|---|---|---|
A B (공백) | 자손(descendant) | A 안 어딘가에 있는 모든 B(손자·증손자 포함) |
A > B | 자식(child) | A의 바로 아래 단계 B만(손자 제외) |
A + B | 인접 형제(adjacent) | A 바로 다음에 붙은 형제 B 하나 |
A ~ B | 일반 형제(sibling) | A 뒤에 오는 모든 형제 B |
[type="text"] | 속성(attribute) | 해당 속성/값을 가진 요소 |
속성 선택자는 값 매칭 방법이 여러 가지입니다(MDN 기준).
| 표기 | 뜻 |
|---|---|
[attr] | 그 속성이 있기만 하면 |
[attr="값"] | 속성 값이 정확히 그 값 |
[attr^="값"] | 그 값으로 시작 |
[attr$="값"] | 그 값으로 끝남 |
[attr*="값"] | 그 값을 포함 |
[attr~="값"] | 공백으로 나뉜 단어 중 하나가 그 값 |
관계 선택자가 어떻게 다른지 직접 봅니다. > 와 공백, + 와 ~ 의 차이에 주목하세요.
3. 가상 클래스 — 상태/순서로 고르기
무엇: 요소의 상태(마우스 올림·입력 중·체크됨)나 형제 중 몇 번째인지로 고릅니다. 콜론 하나(:)로 시작해요.
| 가상 클래스 | 고르는 순간/대상 |
|---|---|
:hover | 마우스 포인터가 올라가 있는 동안 |
:focus | 입력칸·버튼 등이 키보드 초점을 가진 동안 |
:first-child / :last-child | 형제 중 첫째 / 막내 |
:nth-child(n) | 형제 중 n번째(2n 짝수, odd 홀수 등 패턴 가능) |
:not(선택자) | 괄호 안 조건에 해당하지 않는 것 |
:checked | 체크된 체크박스·라디오·선택된 option |
마우스를 올려 보고(hover), 줄무늬(:nth-child)를 확인하고, 체크박스를 켜 보세요(:checked).
4. 가상 요소 — 없던 부분을 만들거나 일부를 고르기
무엇: HTML에 직접 쓰지 않은 가짜 조각을 만들거나(::before/::after), 요소의 특정 부분(입력칸 안내문구·드래그한 글자)을 고릅니다. 콜론 둘(::)로 시작합니다.
| 가상 요소 | 역할 |
|---|---|
::before | 요소 내용 맨 앞에 가짜 조각을 끼움(content 필수) |
::after | 요소 내용 맨 뒤에 가짜 조각을 끼움(content 필수) |
::placeholder | 입력칸의 안내 문구(placeholder) 스타일 |
::selection | 사용자가 마우스로 드래그해 선택한 글자 부분 |
content 속성이 없으면 가상 요소는 화면에 생기지 않습니다. 내용이 없어도 빈 따옴표 content: ""; 라도 적어야 모양(아이콘·뱃지·선)이 나타나요.
아래에서 ::before 로 아이콘과 뱃지를 붙이고, 입력칸 안내문구·드래그 색을 바꿔 봅니다. 결과의 글자를 드래그해 보세요.
5. 우선순위(명시도) — 누구 말이 더 센가
무엇: 한 요소에 여러 규칙이 동시에 걸리면, 명시도(specificity) 점수가 높은 규칙이 이깁니다. MDN 기준으로 점수는 세 칸짜리 (A, B, C) 로 셉니다.
| 칸 | 무엇을 셀까 | 예 |
|---|---|---|
| A (가장 셈) | id 선택자 개수 | #main |
| B | 클래스 · 속성 · 가상 클래스 개수 | .box, [type], :hover |
| C (가장 약함) | 타입(요소) · 가상 요소 개수 | p, ::before |
- 점수는 자릿수처럼 비교합니다. A부터 보고, 같으면 B, 또 같으면 C. (1,0,0) 이 (0,9,9) 보다 무조건 셉니다 — 클래스를 아무리 많이 붙여도 id 하나를 못 이겨요.
- 전체 선택자
*와 결합자(>,+,~, 공백)는 점수에 0 을 더합니다(세지 않음). :not(),:is(),:has()자체는 점수가 없고, 괄호 안에서 가장 센 선택자의 점수만 더해집니다. (:nth-child()같은 보통 가상 클래스는 B 한 칸.)- 인라인 스타일(
style="...")은 위 셋보다 더 셉니다(개념상 맨 앞 칸). - 점수가 완전히 같으면, CSS에서 나중에 쓰인 규칙이 이깁니다(뒤에 온 게 덮어씀).
아래는 같은 요소를 노릴 때의 점수입니다. 위에서 아래로 갈수록 셉니다.
| 선택자 | id(A) | class/속성/가상클래스(B) | 타입/가상요소(C) | 점수 |
|---|---|---|---|---|
* | 0 | 0 | 0 | (0,0,0) |
p | 0 | 0 | 1 | (0,0,1) |
ul li | 0 | 0 | 2 | (0,0,2) |
.box | 0 | 1 | 0 | (0,1,0) |
a:hover | 0 | 1 | 1 | (0,1,1) |
input[type="text"] | 0 | 1 | 1 | (0,1,1) |
.menu .item.active | 0 | 3 | 0 | (0,3,0) |
#main | 1 | 0 | 0 | (1,0,0) |
#main .item p | 1 | 1 | 1 | (1,1,1) |
style="..."(인라인) | 위 모든 선택자보다 셈 | (1,0,0,0) | ||
실제로 점수가 어떻게 승부를 가르는지 봅니다. 같은 글자에 세 규칙이 걸려 있어요 — 누가 이길까요?
선언 끝에 !important 를 붙이면 명시도를 건너뛰고 그 값을 강제로 적용합니다(인라인보다도 셈). 하지만 남용하면 나중에 또 !important 로만 덮을 수 있게 되어 스타일이 엉킵니다.
!important 끼리 부딪히면 그들 사이에서 다시 명시도를 따지고, 그래도 같으면 나중 규칙이 이깁니다. 되도록 쓰지 마세요.
6. 상속과 cascade — 물려받기와 값 키워드
무엇: 어떤 속성은 부모에 준 값이 자식에게 저절로 물려져요(상속). 글자색·글꼴 같은 "글자 관련" 속성이 대표적입니다. 반대로 테두리·여백·배경 같은 "상자 관련" 속성은 보통 상속되지 않습니다.
- cascade(폭포) 란 여러 출처(브라우저 기본·작성자 CSS·인라인)와 규칙이 위에서 아래로 흘러 쌓이며 최종 값을 정하는 과정을 말합니다. 그 안에서 승부를 가르는 잣대가 앞서 본 명시도와 순서예요.
inherit— 부모의 그 속성 값을 강제로 물려받음(원래 상속 안 되는 속성에도 적용 가능).initial— 그 속성의 CSS 기본값으로 되돌림(브라우저 스타일이 아니라 명세상 초기값).unset— 상속되는 속성이면inherit처럼, 아니면initial처럼 동작(상황에 맞게 알아서).- (참고)
revert— 브라우저/사용자 기본 스타일 단계로 되돌림.
부모에 색을 주면 자식이 물려받고, 자식이 inherit/initial/unset 으로 어떻게 달라지는지 봅니다.
한눈에 — 선택자 카탈로그
| 선택자 | 분류 | 고르는 대상 |
|---|---|---|
* | 전체 | 모든 요소(명시도 0) |
p | 타입 | 해당 태그 전부 |
.box | 클래스 | 그 클래스가 붙은 요소 |
#main | id | 그 id 인 요소(하나) |
A, B | 그룹 | A·B 모두에 같은 규칙 |
A B | 자손 | A 안의 모든 B |
A > B | 자식 | A 바로 아래 B |
A + B | 인접 형제 | A 바로 다음 B 하나 |
A ~ B | 일반 형제 | A 뒤의 모든 형제 B |
[attr], [attr="v"], [attr^=], [attr$=], [attr*=] | 속성 | 속성 유무/값 일치/시작/끝/포함 |
:hover, :focus | 가상 클래스(상태) | 마우스 올림 / 초점 |
:first-child, :last-child, :nth-child(n) | 가상 클래스(순서) | 첫째 / 막내 / n번째 |
:not(), :checked | 가상 클래스 | 제외 / 체크됨 |
::before, ::after | 가상 요소 | 앞/뒤 가짜 조각(content 필수) |
::placeholder, ::selection | 가상 요소 | 안내문구 / 드래그한 글자 |
유지보수가 쉬운 CSS의 비결은 명시도를 일부러 낮게 유지하는 것입니다.
되도록 클래스 하나로 스타일을 주고(.btn-primary 식), #id 선택자나 div.wrap ul li.item a 처럼 길게 겹친 선택자, 그리고 !important 는 피하세요.
명시도가 낮고 비슷하면 나중에 덮어쓰기 쉬워서, 새 규칙 하나 추가하는 일이 간단해집니다. "이기려고" 선택자를 더 길게 만드는 순간 악순환이 시작돼요.
다음 단계
- 고른 요소의 크기·여백·테두리를 다루는 박스 모델 → CSS · 박스 모델