C.W.K.
Stream
Lesson 02 of 05 · published

Specificity: 동점을 결정하는 숫자 점수

~12 min · specificity, cascade, important

Level 0Markup Novice
0 XP0/34 lessons0/12 achievements
0/100 XP to next level100 XP to go0% complete
"!important 는 specificity 이해 못 했을 때의 답이야. 답은 specificity 를 이해하는 거."

Specificity 가 뭐야?

Specificity 는 CSS 가 각 selector 에 계산하는 숫자 점수. 여러 규칙이 충돌하는 선언으로 같은 element 를 타깃할 때, 가장 높은 specificity 가진 selector 가 이김. Specificity 동점이면 source 순서에서 나중에 오는 규칙이 이김.

그게 알고리즘 전체. 어려운 부분은 맞게 계산하는 거.

네 컬럼 점수: (a, b, c, d)

Specificity 는 네 카운트의 튜플, 왼쪽-오른쪽으로 비교:

  • a — 인라인 스타일: 선언이 style="..." attribute 안이면 1, 아니면 0.
  • b — ID: #id selector 개수.
  • c — class, attribute, pseudo-class: .class, [attr], :hover, :nth-child 등의 개수.
  • d — element 와 pseudo-element: div, ::before 등의 개수.

튜플 비교 왼쪽-오른쪽: a 의 어떤 차이든 결정; a 동점이면 b 봐; 이런 식. 이건 10 진수 산술이 아니야. Class 10 개 (0,0,10,0) 가 ID 1 개 (0,1,0,0) 안 이김 — ID 가 더 높은 컬럼.

손으로 계산한 예

  • p → (0,0,0,1)
  • p.lead → (0,0,1,1)
  • article p.lead → (0,0,1,2)
  • article p.lead.featured → (0,0,2,2)
  • #main p → (0,1,0,1)
  • nav a:hover → (0,0,1,2)
  • a[href^="https"] → (0,0,1,1)
  • style="color: red" → (1,0,0,0)
  • :where(.btn, .button) → (0,0,0,0) — :where 는 항상 0 기여
  • :is(.btn, #cta) → (0,1,0,0) — :is 는 가장 높은 argument 의 specificity 받음
  • :not(.btn, #cta) → (0,1,0,0) — :is 와 같은 규칙

!important override

선언에 !important 더하면 cascade 의 더 높은 tier 로 이동 — specificity 와 무관하게 모든 non-!important 선언 이김. !important 두 개는 다시 specificity 로 경쟁.

!important 는 거의 항상 잘못된 답. "왜 내 규칙이 안 이기는지 못 알아내서 escalate 했음" 신호. 옳은 답은 보통:

  • 본인 selector 의 specificity 를 경쟁자에 맞게 올림.
  • 충돌하는 규칙을 덜 specific 하게 리팩토링.
  • Cascade layer (Track 7 의 레슨) 써서 순서 명시적으로 제어.

!important 의 정당한 사용은 좁아: 접근성 위해 작성자 스타일 override 해야 하는 사용자 스타일시트, 항상 이기게 의도된 utility 클래스 (Tailwind 의 plugin override 가 이렇게 함), 그리고 가끔의 서드파티 라이브러리 override.

디자인 시스템용 :where() 마법

:where():is() 와 같은 selector 매치하지만 항상 specificity (0,0,0,0) 보고. 디자인 시스템이 필요로 하는 정확히 그것: 어떤 작성자 규칙이 단일 class 로 override 할 수 있는 베이스 스타일:

Specificity 는 힘이 아니라 commitment 이야. Specificity 올릴 때마다 규칙을 override 하기 어렵게 만들어. 일을 하는 가장 낮은 specificity 규칙이 가장 유연. 측정해서 낮은 게 안 됐을 때만 높은 specificity 로.

DevTools 에서 Specificity 읽기

Chrome DevTools 가 Styles 패널에서 각 규칙 옆에 specificity 보여 줘: selector 텍스트 hover 하면 툴팁이 (a,b,c,d) 점수 표시. Firefox 도 비슷. 디버깅 가장 빠른 방법: "왜 파란 스타일이 이기지? 아, 저 다른 규칙에 못 본 ID 가 있었네."

Cascade 순서, 전체

두 규칙이 적용되면, 브라우저가 이 정렬된 리스트 걷고 첫 결정적 비교가 이김:

  1. Origin 과 importance — user-important > author-important > author > user > UA-default (그리고 UA-important 는 모든 user 아래).
  2. Cascade layer — 늦게 선언된 layer 가 일찍 선언된 거 이김 (Track 7).
  3. Specificity — 방금 계산한 점수.
  4. Source 순서 — 늦게 쓴 규칙이 이김.

피파의 노트

피파의 WebUI 가 Tailwind 써, 모든 utility 클래스가 (0,0,1,0). 충돌이 거의 항상 source 순서로 줄어 — Tailwind 의 빌드가 결정적 순서로 클래스 발행. 반면 cwk-site 에세이는 specificity 가 중요한 작은 custom CSS 모듈 써; 그건 코드베이스가 베이스 스타일에 :where() 쓰고 ID 는 전혀 피해. 다른 스택, 같은 교훈: specificity 전략 골라서 고수.

Code

단계별 specificity·css
/* 머릿속에서 specificity 계산 */

/* (0,0,0,1) — type 만 */
h1 { color: black; }

/* (0,0,1,1) — type + class */
h1.title { color: navy; }

/* (0,0,1,2) — type 둘 + class */
article h1.title { color: forest; }

/* (0,1,0,1) — id + type, id 없는 어떤 것도 이김 */
#main h1 { color: maroon; }

/* (1,0,0,0) — 인라인 스타일, 거의 모든 거 이김 */
/* <h1 style="color: tomato;"> */

/* 결과: 다섯 규칙 모두 활성일 때 인라인 스타일이 이김.
   인라인 제거 → #main h1 이김.
   #main 제거 → article h1.title 이김.
   article h1.title 제거 → h1.title 이김.
   h1.title 제거 → h1 이김. */
디자인 시스템 베이스용 :where()·css
/* 흔한 :is() vs :where() 차이 */

/* Specificity (0,1,0,1) — id 가 기여 */
:is(article, section, #featured) h1 { font-weight: 700; }

/* Specificity (0,0,0,1) — :where 에서 0, 그냥 h1 */
:where(article, section, #featured) h1 { font-weight: 700; }

/* 디자인 시스템 베이스 — 손쉽게 override 가능 */
:where(.btn) {
  padding: 0.5rem 1rem;
  border-radius: 4px;
  background: #5BA3D8;
  color: white;
}

/* 앱 작성자가 (0,0,1,0) 으로 override — specificity 전쟁 안 필요 */
.primary { background: #FF7A52; }
!important escalation vs cascade layer·css
/* !important escalation 패턴 (가능하면 피해) */

/* 라이브러리 스타일 */
.toast { background: red !important; }

/* 앱 override 시도 — 라이브러리에 !important 있어서 실패 */
.toast.success { background: green; }

/* 앱 escalation — 작동하지만 못생김 */
.toast.success { background: green !important; }

/* 더 나은 fix: cascade layer 써 (Track 7) */
@layer library, app;
@layer library { .toast { background: red; } }
@layer app { .toast.success { background: green; } }
/* App layer 가 specificity 와 무관하게 이김 — !important 안 필요 */

External links

Exercise

어떤 production 스타일시트 (DevTools → Sources → 아무 .css 파일) 에서든 CSS 규칙 셋 골라. 각자 (a,b,c,d) 점수 손으로 계산. DevTools 에서 selector hover 해서 검증. 그러고 나서 기대한 대로 적용 안 되는 규칙 골라. Cascade 걸어 봐: 더 높은 specificity 가진 어떤 규칙이 override 중? 옳은 fix 가 본인 거 올리기, 그들 거 낮추기, :where() 로 리팩토링 중 어느 거?
Hint
Override 가 서드파티 라이브러리에서 오면 보통 그들 거 리팩토링 못 함. Cascade layer (Track 7 옆) 가 원칙적 해결책; 본인 specificity 를 맞추게 올리는 건 소방 해결책.

Progress

Progress is local-only — sign in to sync across devices.
이 페이지에서 버그를 발견하셨거나 피드백이 있으세요?문제 신고

댓글 0

🔔 답글 알림 (로그인 필요)
로그인댓글을 남기려면 로그인해 주세요.

아직 댓글이 없어요. 첫 댓글을 남겨보세요.