스타일 · CSS · 선택자 · 우선순위

CSS · 선택자와 우선순위

CSS? 는 "누구를 골라서 어떤 모양으로 바꿀까"를 적는 언어입니다. 여기서 "누구를 고르는 부분"이 선택자(selector) 예요. 그리고 한 요소에 서로 다른 규칙이 동시에 걸리면 누구 말을 들을지 정하는 규칙이 우선순위(명시도) 입니다. 이 페이지는 글로만 설명하지 않고, 칸마다 직접 고쳐서 바로 화면을 보는 라이브 예시를 함께 둡니다.

선택자는 교실에서 "2번 줄에 앉은 학생", "안경 쓴 학생"처럼 대상을 지목하는 말이고, 우선순위는 두 선생님이 서로 다른 지시를 했을 때 "누구 말이 더 센가"를 가르는 서열 규칙이라고 보면 됩니다.
라이브 예시 읽는 법

아래 회색 상자(라이브 데모)는 왼쪽에 진짜 HTML? + CSS 코드, 오른쪽에 그 결과 화면이 나옵니다. 코드를 직접 고치고 실행을 누르면 화면이 바뀌어요. 마우스를 올리거나(hover) 입력칸을 클릭(focus)해서 반응도 확인해 보세요. 읽기용으로만 적힌 코드는 흰 상자에 들어 있습니다.

1. 선택자 기본 — 타입·클래스·id·전체·그룹

무엇: 가장 기본이 되는 다섯 가지 "지목하는 법"입니다.

선택자읽는 법고르는 대상
p타입(요소) 선택자모든 <p> 요소
.box클래스 선택자class="box" 가 붙은 모든 요소
#mainid 선택자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 값은 한 페이지에 하나만 있어야 합니다. 여러 요소를 같은 모양으로 만들고 싶으면 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사용자가 마우스로 드래그해 선택한 글자 부분
::before / ::after 에는 content 가 꼭 필요

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)점수
*000(0,0,0)
p001(0,0,1)
ul li002(0,0,2)
.box010(0,1,0)
a:hover011(0,1,1)
input[type="text"]011(0,1,1)
.menu .item.active030(0,3,0)
#main100(1,0,0)
#main .item p111(1,1,1)
style="..."(인라인)위 모든 선택자보다 셈(1,0,0,0)

실제로 점수가 어떻게 승부를 가르는지 봅니다. 같은 글자에 세 규칙이 걸려 있어요 — 누가 이길까요?

!important 는 최후의 수단

선언 끝에 !important 를 붙이면 명시도를 건너뛰고 그 값을 강제로 적용합니다(인라인보다도 셈). 하지만 남용하면 나중에 또 !important 로만 덮을 수 있게 되어 스타일이 엉킵니다. !important 끼리 부딪히면 그들 사이에서 다시 명시도를 따지고, 그래도 같으면 나중 규칙이 이깁니다. 되도록 쓰지 마세요.

6. 상속과 cascade — 물려받기와 값 키워드

무엇: 어떤 속성은 부모에 준 값이 자식에게 저절로 물려져요(상속). 글자색·글꼴 같은 "글자 관련" 속성이 대표적입니다. 반대로 테두리·여백·배경 같은 "상자 관련" 속성은 보통 상속되지 않습니다.

  • cascade(폭포) 란 여러 출처(브라우저 기본·작성자 CSS·인라인)와 규칙이 위에서 아래로 흘러 쌓이며 최종 값을 정하는 과정을 말합니다. 그 안에서 승부를 가르는 잣대가 앞서 본 명시도와 순서예요.
  • inherit — 부모의 그 속성 값을 강제로 물려받음(원래 상속 안 되는 속성에도 적용 가능).
  • initial — 그 속성의 CSS 기본값으로 되돌림(브라우저 스타일이 아니라 명세상 초기값).
  • unset — 상속되는 속성이면 inherit 처럼, 아니면 initial 처럼 동작(상황에 맞게 알아서).
  • (참고) revert — 브라우저/사용자 기본 스타일 단계로 되돌림.

부모에 색을 주면 자식이 물려받고, 자식이 inherit/initial/unset 으로 어떻게 달라지는지 봅니다.

한눈에 — 선택자 카탈로그

선택자분류고르는 대상
*전체모든 요소(명시도 0)
p타입해당 태그 전부
.box클래스그 클래스가 붙은 요소
#mainid그 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 는 피하세요. 명시도가 낮고 비슷하면 나중에 덮어쓰기 쉬워서, 새 규칙 하나 추가하는 일이 간단해집니다. "이기려고" 선택자를 더 길게 만드는 순간 악순환이 시작돼요.

다음 단계