Learning
레슨 10 / 13·4개 토픽

CSS 변수와 특이도

CSS 커스텀 속성 (변수)

CSS 커스텀 속성(Custom Properties)은 --로 시작하는 사용자 정의 변수입니다. var() 함수로 값을 참조하며, 테마 색상, 간격, 폰트 크기 등을 일관되게 관리할 수 있습니다. JavaScript로도 읽고 쓸 수 있어 동적 테마 전환에 매우 유용합니다.

css
/* ── 커스텀 속성 선언과 사용 ── */
:root {
  /* 색상 팔레트 */
  --color-primary: #10b981;
  --color-primary-dark: #059669;
  --color-bg: #ffffff;
  --color-text: #111827;

  /* 간격 */
  --spacing-sm: 8px;
  --spacing-md: 16px;
  --spacing-lg: 32px;

  /* 폰트 */
  --font-size-base: 16px;
  --font-size-lg: 20px;
  --border-radius: 8px;
}

/* 변수 사용 */
.button {
  background: var(--color-primary);
  color: var(--color-bg);
  padding: var(--spacing-sm) var(--spacing-md);
  border-radius: var(--border-radius);
  font-size: var(--font-size-base);
}

.button:hover {
  background: var(--color-primary-dark);
}

다크 테마 구현

커스텀 속성의 가장 큰 장점은 테마 전환입니다. :root에 기본 테마 값을 정의하고, 다크 모드 클래스나 미디어 쿼리에서 값을 덮어쓰면 전체 사이트의 색상이 한꺼번에 바뀝니다.

css
/* 라이트 테마 (기본) */
:root {
  --color-bg: #ffffff;
  --color-surface: #f9fafb;
  --color-text: #111827;
  --color-text-muted: #6b7280;
  --color-border: #e5e7eb;
}

/* 다크 테마 — 클래스로 전환 */
.dark {
  --color-bg: #111827;
  --color-surface: #1f2937;
  --color-text: #f9fafb;
  --color-text-muted: #9ca3af;
  --color-border: #374151;
}

/* 또는 시스템 설정을 따르기 */
@media (prefers-color-scheme: dark) {
  :root {
    --color-bg: #111827;
    --color-surface: #1f2937;
    --color-text: #f9fafb;
    --color-text-muted: #9ca3af;
    --color-border: #374151;
  }
}

/* 모든 컴포넌트에서 변수만 사용 */
body {
  background: var(--color-bg);
  color: var(--color-text);
}

.card {
  background: var(--color-surface);
  border: 1px solid var(--color-border);
}
javascript
// JavaScript로 CSS 변수 읽기/쓰기
const root = document.documentElement;

// 읽기
const primary = getComputedStyle(root)
  .getPropertyValue('--color-primary');

// 쓰기 — 실시간으로 테마 색상 변경
root.style.setProperty('--color-primary', '#3b82f6');

// 다크 모드 토글
document.body.classList.toggle('dark');

CSS 특이도 (Specificity)

여러 CSS 규칙이 같은 요소에 적용될 때, 브라우저는 특이도(Specificity) 점수로 우선순위를 결정합니다. 특이도는 4자리 숫자 (인라인, ID, 클래스, 요소) 형태로 계산됩니다. 점수가 같으면 나중에 선언된 규칙이 적용됩니다.

  • 인라인 스타일 (style="...") → 1,0,0,0 — 가장 높음
  • ID 선택자 (#header) → 0,1,0,0
  • 클래스/속성/가상클래스 (.btn, [type], :hover) → 0,0,1,0
  • 요소/가상요소 (div, p, ::before) → 0,0,0,1
  • 전체 선택자 (*) → 0,0,0,0 — 특이도 없음
css
/* ── 특이도 점수 비교 예시 ── */

/* 0,0,0,1 — 요소 선택자 */
p { color: black; }

/* 0,0,1,0 — 클래스 선택자 (위의 p보다 우선) */
.highlight { color: blue; }

/* 0,0,1,1 — 요소 + 클래스 */
p.highlight { color: green; }

/* 0,1,0,0 — ID 선택자 (클래스보다 우선) */
#main-text { color: red; }

/* 0,1,1,1 — ID + 클래스 + 요소 */
#main-text p.highlight { color: purple; }

/* ── 같은 특이도일 때: 나중에 선언된 것이 이김 ── */
.box { color: blue; }
.box { color: red; }   /* ← 이것이 적용됨 */

캐스케이드와 상속

CSS의 "C"는 Cascading(계단식)을 뜻합니다. 브라우저는 다음 순서로 스타일 우선순위를 결정합니다: ① !important ② 인라인 스타일 ③ 특이도 점수 ④ 소스 순서. 상속(Inheritance)은 부모 요소의 일부 속성이 자식에게 전달되는 것으로, colorfont-* 계열은 상속되지만 margin, padding, border는 상속되지 않습니다.

css
/* !important는 특이도를 무시하고 최우선 적용 */
.button {
  color: blue !important;  /* 어떤 규칙보다 우선 */
}

#nav .button {
  color: red;  /* 특이도가 더 높지만, !important에 밀림 */
}

/* ⚠️ !important 남용 금지 — 디버깅이 매우 어려워집니다 */

/* 상속 제어 키워드 */
.child {
  color: inherit;   /* 부모 값 상속 */
  margin: initial;  /* 브라우저 기본값으로 리셋 */
  padding: unset;   /* 상속 가능하면 inherit, 아니면 initial */
}
💡

!important는 최후의 수단으로만 사용하세요. 특이도 문제를 근본적으로 해결하려면 선택자를 단순하게 유지하는 것이 좋습니다. BEM(.block__element--modifier) 같은 네이밍 컨벤션을 사용하면 클래스 기반으로 일관된 특이도를 유지할 수 있습니다.