06 — Theming (Dark Mode · Multi-Brand · Contextual Tokens)
이 문서가 답하는 질문: 디자인 시스템에서 “테마”란 정확히 무엇을 바꾸는 것인가? 다크모드, 멀티 브랜드, RTL,
prefers-reduced-motion— 이 다섯 가지는 같은 메커니즘으로 풀 수 있는가, 아니면 각자 다른 도구가 필요한가? 한 줄 답 (Pyramid Top): 테마는 “컴포넌트 코드를 한 줄도 바꾸지 않고 semantic 토큰만 swap하는 컨텍스트” 다 —primitive토큰(blue.500)은 절대 변하지 않고,semantic토큰(color.bg.surface)이 라이트/다크/브랜드에서 다른 primitive를 가리킨다. CSS variables +[data-theme]selector가 사실상 표준이고, Tailwind v4의@theme와 Panda의conditions가 이 패턴을 1급 시민으로 끌어올렸다.
이 챕터의 위치
01-tokens가 토큰의 형식(DTCG, 3-tier 계층), 02-color-system이 색의 수학(OKLCH, 12-step)을 다뤘다면, 이 챕터는 “같은 컴포넌트가 언제 어느 토큰 값을 쓸 것인가” 를 다룬다.
| 컨텍스트 | 신호(signal) | 바뀌는 토큰 | 바뀌지 않는 것 |
|---|---|---|---|
| 다크모드 | prefers-color-scheme: dark / [data-theme="dark"] | color.bg.*, color.fg.*, color.border.* | spacing, radius, font-size |
| 멀티 브랜드 | [data-brand="admin"] vs [data-brand="storefront"] | color.accent.*, font.family.brand | 레이아웃, 컴포넌트 구조 |
| RTL | dir="rtl" | (대부분 토큰은 그대로) | logical properties가 자동 처리 |
| 고대비 | prefers-contrast: more, forced-colors: active | color.fg.*, color.border.* | 레이아웃 |
| 감속 모션 | prefers-reduced-motion: reduce | duration.*, transition.* | 색·spacing |
핵심 통찰 한 줄: 컴포넌트 토큰(button.bg)을 분기하지 말고, semantic 토큰(color.bg.accent)에서만 분기하라. 100개 컴포넌트가 깨질지, 토큰 한 줄로 끝날지의 차이.
챕터 지도 (Mermaid)
챕터 인덱스
| # | 파일 | 다루는 것 | 핵심 키워드 |
|---|---|---|---|
| 01 | 01-theme-as-context | 테마 = “토큰 값을 바꾸는 컨텍스트”. semantic만 분기, component 토큰은 그대로 | 3-tier, semantic, swap point |
| 02 | 02-dark-mode-strategy | prefers-color-scheme vs [data-theme] vs .dark, FOUC, color-scheme property | data-theme, blocking script, color-scheme |
| 03 | 03-multi-brand-systems | Atlassian의 product brands, Shopify의 admin/storefront, 하나의 컴포넌트 라이브러리 + 여러 brand 토큰 | data-brand, brand scope, primitive 공유 |
| 04 | 04-runtime-vs-buildtime-theming | CSS vars 런타임 swap vs Tailwind v3 class 빌드, Tailwind v4 @theme | runtime, buildtime, CSS bloat |
| 05 | 05-panda-tailwind-theme-config | Panda conditions, Tailwind darkMode: 'class', 둘 다 작동하는 패턴 | _dark, darkMode, custom condition |
| 06 | 06-rtl-and-localization | RTL 지원, logical properties, locale-specific font stack | margin-inline-start, dir, CJK font |
| 07 | 07-system-preferences | prefers-reduced-motion, prefers-contrast, forced-colors (WHCM) | a11y, Canvas, CanvasText |
핵심 원칙 (5개)
- 분기는 semantic 층에서만 —
button.bg를 다크에서 분기하지 말고color.bg.accent한 곳에서만. - CSS variables가 런타임 테마의 1급 시민 — Tailwind v3 class swap은 CSS 크기 2배의 함정.
- FOUC는 SSR의 약점, blocking
<script>로 막는다 —<html>렌더 전에data-theme설정. color-schemeproperty를 잊지 말 것 — 스크롤바, form control도 다크로 가도록.forced-colors(Windows High Contrast)는 토큰을 무시한다 — system color (Canvas,CanvasText)에 의지하라.
What-if — 이 챕터의 한계
- 테마 전환 UX (토글 버튼 디자인, 애니메이션)는
04-recipes-variants또는 컴포넌트 카탈로그 영역. - i18n 텍스트 번역은 다루지 않는다 — 토큰화 가능한 시각 자산(font stack, direction)만.
- Figma Variables의 modes UI 조작법은
08-pipeline-distribution에서.
요약
- 테마 = semantic 토큰을 swap하는 컨텍스트 — primitive와 component 토큰은 변하지 않는다.
- 다크모드, 멀티 브랜드, RTL, system preferences는 모두 같은 메커니즘(CSS variables + selector)으로 풀린다.
- Tailwind v4의
@theme와 Panda의conditions가 이 메커니즘을 1급 시민으로 끌어올렸다.