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

Custom Property: live, scoped, 상속 가능한 디자인 토큰

~12 min · custom-properties, variables, design-tokens, property

Level 0Markup Novice
0 XP0/34 lessons0/12 achievements
0/100 XP to next level100 XP to go0% complete
"Custom property 가 CSS 모자 쓴 Sass 변수가 아냐. 브라우저가 resolve 하는 live 값, 정의한 element 에 scoped, DOM 통해 상속 가능. Mental model 전환이 이걸 풀어내."

Sass 변수가 못 하는 세 가지

  1. DOM 통해 상속. :root--accent: blue 설정; 모든 자손이 봄. .danger 에 재정의; 그 subtree 만 새 값 봄. Sass 변수는 build 시간에 파일 scope.
  2. 런타임에 변경. JavaScript 가 어떤 element 의 custom property 든 업데이트 가능 (element.style.setProperty('--theme', 'dark')) 하고 모든 의존 규칙이 즉시 업데이트. Sass 변수는 출력 CSS 에 baked.
  3. Media query 와 selector 가 쿼리. @media (prefers-color-scheme: dark) 안 정의된 custom property 가 사용자 dark 모드일 때만 적용. Sass 변수 못 함 — 브라우저가 보기 전에 resolve.

정의와 사용

  • 정의: 어떤 selector 든 --name: value;. 전역 토큰엔 관례적으로 :root.
  • 소비: cascade 어디서든 property: var(--name);.
  • Fallback: --name 안 정의됐을 때 var(--name, fallback)fallback 사용.
  • Composition: var(--a, var(--b, default)) 가 fallback 중첩.

Scoping: 파워 무브

Custom property 가 DOM 통해 상속. Element 에 property 재정의가 그 element AND 모든 자손의 값 변경:

제대로 된 테마

완전 테마 시스템 위해 custom property 를 media query 와 class 토글과 결합:

@property: 타입 있는 custom property

기본적으로 custom property 는 타입 없는 string. --gap: anything 도 되고 브라우저가 검증 안 함. @property 가 타입, 기본값, 상속 행동 가진 custom property 등록:

혜택: 타입 체킹 (유효하지 않은 값 거부), 애니메이션 지원 (등록 안 된 custom property 가 부드럽게 애니메이트 안 됨 — 브라우저가 anything 의 interpolate 방법 모름), 그리고 :root 정의 의존 안 하는 명시적 기본값.

JavaScript 통합

JS 가 custom property 읽고 쓸 수 있어:

흔한 디자인 토큰 패턴

  • 색: --bg, --fg, --accent, --accent-secondary, --border.
  • Spacing 스케일: --space-1: 0.25rem; --space-2: 0.5rem; --space-4: 1rem; --space-8: 2rem; (기하 진행).
  • Radii: --radius-sm: 4px; --radius-md: 8px; --radius-lg: 16px;.
  • Shadow: --shadow-sm, --shadow-md, --shadow-lg.
  • Type 스케일: --text-xs, --text-sm, --text-base, --text-lg, --text-xl.
  • Z-index layer: --z-dropdown, --z-modal, --z-toast.

:root 에 정의. 사이트의 디자인 시스템이 한 CSS 파일의 상단 섹션에 살음.

:root 에 토큰; semantic 이름이 literal 이름 이김. --color-action > --blue-500. 전자가 리브랜딩에서 살아남음; 후자가 200 참조 이름 바꾸기 필요. Literal 색을 한 layer 로, 그것 참조하는 semantic 토큰을 다른 layer 로 정의.

피파의 노트

피파의 WebUI 의 모든 테마 — dark/light, brain-color accent, council-mode 하이라이트 — 가 전적으로 custom property 로 구현. 베이스 테마가 :root 에 약 40 변수 정의; dark 테마가 그 중 12 재정의. 테마 전환이 <html>data-theme 속성 변경 하나; 모든 component 가 업데이트 — 하드코딩된 색 대신 var(--bg) 읽어서. 같은 아키텍처가 cwk-site 의 per-quest 팔레트 power — 각 quest 의 meta.json 팔레트가 quest 페이지 wrapper 의 scoped custom property 됨.

Code

:root 의 디자인 토큰·css
/* :root 에 토큰 정의 */
:root {
  /* 색 토큰 — semantic, literal 아님 */
  --color-bg: #ffffff;
  --color-fg: #1c2230;
  --color-accent: #5BA3D8;
  --color-danger: #d93b3b;
  --color-border: #e0e6ed;

  /* Spacing 스케일 (기하, base 0.25rem) */
  --space-1: 0.25rem;
  --space-2: 0.5rem;
  --space-4: 1rem;
  --space-8: 2rem;

  /* Type 스케일 */
  --text-sm: 0.875rem;
  --text-base: 1rem;
  --text-lg: 1.25rem;
  --text-xl: 1.5rem;

  /* Radii, shadow 등 */
  --radius-md: 8px;
  --shadow-md: 0 2px 8px rgba(0,0,0,0.1);
}

/* 어디서나 소비 */
body { background: var(--color-bg); color: var(--color-fg); }
.btn { padding: var(--space-2) var(--space-4); border-radius: var(--radius-md); }
테마 + scoped 재정의·css
/* 재정의 통한 테마 */

/* Light 모드 (기본) */
:root {
  --bg: #ffffff;
  --fg: #1c2230;
  --accent: #1F5F8B;
}

/* Dark 모드 — 같은 토큰, 다른 값 */
[data-theme="dark"] {
  --bg: #0b0e14;
  --fg: #e7ecf3;
  --accent: #5BA3D8;
}

/* 또는 prefers-color-scheme 통해 */
@media (prefers-color-scheme: dark) {
  :root {
    --bg: #0b0e14;
    --fg: #e7ecf3;
    --accent: #5BA3D8;
  }
}

/* Scoping: 섹션이 subtree 용 override */
.alert-danger {
  --accent: var(--color-danger);  /* 이 영역 danger 색 */
  --bg: #fef2f2;
}
.alert-danger .btn {
  background: var(--accent);      /* 재정의된 값 받음 */
}
@property — 타입 있는 custom property·css
/* @property — 타입 있고, 기본값 있고, 애니메이트 가능 */

@property --gradient-angle {
  syntax: '<angle>';
  initial-value: 0deg;
  inherits: false;
}

.rotating-border {
  background: linear-gradient(var(--gradient-angle), red, blue);
  animation: rotate 4s linear infinite;
}

@keyframes rotate {
  to { --gradient-angle: 360deg; }
}

/* @property 없으면 이 애니메이션 작동 안 함 — 브라우저가
   타입 없는 custom property interpolate 방법 모름. @property 가
   <angle> 로 선언하면 브라우저가 0deg 에서 360deg 로 부드럽게
   tween 가능. */

External links

Exercise

작은 테마 시스템 짓기: :root 에 색 토큰 5-10, spacing 스케일, 타이포그래피 스케일 정의. [data-theme="dark"] override 통해 dark 테마 추가. JavaScript 로 테마 토글하는 버튼 추가. Sibling 영향 안 주고 --accent 로컬 재정의하는 alert 영역 추가. DevTools 에서 테마 전환이 모든 component 자동 업데이트하는지 확인.
Hint
CSS 변수를 구체적 스타일에 사용 (.btn { background: var(--color-accent); padding: var(--space-2) var(--space-4); }). 토글은 그냥 document.documentElement.dataset.theme = isDark ? 'dark' : 'light'. 상속 + scoping 이 나머지 처리.

Progress

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

댓글 0

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

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