디자인 토큰
Primitive → Semantic → Component 3계층 토큰의 구조와 규칙, Web/Flutter 소비 방법. 한 곳(JSON SSOT)을 고치면 두 플랫폼이 함께 바뀝니다.
마지막 업데이트 2026-06-12
3계층 구조#
토큰 정본은 tokens/ 디렉토리의 W3C Design Tokens JSON 하나입니다(AD-2).
codegen(make tokens)이 CSS 변수와 Dart 상수를 동시에 생성합니다.
Primitive(core) — 원시 값. 램프와 스케일 → Semantic — 의도. 라이트/다크 2세트 → Component — 컴포넌트별 결정
| 계층 | 참조 규칙 | 위반 시 |
|---|---|---|
| core | literal만 (alias 금지) | 빌드 실패 |
| semantic | core만 참조 | 빌드 실패 |
| component | semantic만 참조 (primitive 직접 참조 금지 — AD-2 #4) | 빌드 실패 |
토널 램프 (Primitive 생성)#
core 램프는 손으로 고른 hex가 아니라 톤 수학으로 생성됩니다(ADR-013). 톤 번호는
곧 CIELAB L*(지각 명도, D65) — t40은 모든 hue에서 같은 명도라, 두 색의 대비가
톤 차이만으로 결정됩니다. Material 3의 tonal palette와 같은 정의를 HCT 엔진 없이
scripts/build-tokens/src/tonal.ts(culori lch65)로 구현했습니다.
- t22다크 surface-raised (tone 수학 파생)
- t40M3 라이트 전경 기준
- t80M3 다크 전경 기준
톤 번호 = CIELAB L*(지각 명도, D65) — 0 검정 → 100 흰색. 같은 번호는 모든 hue에서 같은 명도라 대비가 톤 차이로 결정됩니다.
색 램프는 표준 13톤입니다 — t40(라이트 기준)과 t80(다크 기준)의 대칭이 테마 전환
공식이 됩니다.
- t40라이트 primary
- t80다크 primary
톤 번호 = CIELAB L*(지각 명도, D65) — 0 검정 → 100 흰색. 같은 번호는 모든 hue에서 같은 명도라 대비가 톤 차이로 결정됩니다.
톤 번호 = CIELAB L*(지각 명도, D65) — 0 검정 → 100 흰색. 같은 번호는 모든 hue에서 같은 명도라 대비가 톤 차이로 결정됩니다.
톤 번호 = CIELAB L*(지각 명도, D65) — 0 검정 → 100 흰색. 같은 번호는 모든 hue에서 같은 명도라 대비가 톤 차이로 결정됩니다.
톤 번호 = CIELAB L*(지각 명도, D65) — 0 검정 → 100 흰색. 같은 번호는 모든 hue에서 같은 명도라 대비가 톤 차이로 결정됩니다.
톤 번호 = CIELAB L*(지각 명도, D65) — 0 검정 → 100 흰색. 같은 번호는 모든 hue에서 같은 명도라 대비가 톤 차이로 결정됩니다.
램프는 승인된 기존 팔레트의 실측 (L*, C, H) 프로파일을 보간해 생성합니다 — 체계화이지
리베이스가 아닙니다. 빌드 게이트가 앵커 충실도(기존 색을 채널 Δ≤2로 재현)와 톤 대비
법칙(Δtone≥50 → 4.4:1, 명시 전경/배경 페어 → 4.5:1)을 매 빌드 검증합니다. 새 브랜드
컬러는 프로파일 한 줄이면 전체 톤·상태 레이어가 자동 파생됩니다.
네이밍#
JSON 경로가 그대로 CSS 변수명과 Dart 상수명이 됩니다.
| JSON 경로 | CSS | Dart |
|---|---|---|
blue.600 | --wds-blue-600 | WizPalette.blue600 |
color.primary | --wds-color-primary | WizColors.light.primary / .dark.primary |
font.size.body-1 | --wds-font-size-body-1 | WizTypography.sizeBody1 |
space.4 | --wds-space-4 | WizSpacing.s4 |
button.bg | --wds-button-bg | WizButtonTokens.light.bg |
사용법#
Web — CSS 변수#
alias는 var() 참조로 유지됩니다(테마 전환은 [data-theme]가 처리).
.card {
background: var(--wds-color-surface);
border-radius: var(--wds-radius-md);
padding: var(--wds-space-6);
box-shadow: var(--wds-shadow-sm);
}
Flutter — 생성 상수#
Dart 타겟은 빌드타임에 평면화됩니다 — rem은 px(×16) double로, shadow는
BoxShadow로, duration은 Duration으로, 이징은 Cubic으로 변환됩니다.
import 'package:wiz_ui/wiz_ui.dart';
Container(
color: WizColors.light.surface, // 다크: WizColors.dark.surface
padding: const EdgeInsets.all(WizSpacing.s6),
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(WizRadius.md),
boxShadow: const [WizShadows.sm],
),
)
두 타겟의 일치는 교차 타겟 테스트가 보증합니다 — 모든 시멘틱 컬러의 light/dark computed 값이 CSS와 Dart에서 동일함을 매 빌드 검증.
시멘틱 컬러 전체#
라이트가 정본, 다크는 오버라이드입니다. 상세 설계 근거는 다크 모드 참조.
| 토큰 | 라이트 | 다크 | 설명 |
|---|---|---|---|
| — | |||
| — | |||
| — | |||
| — | |||
| AA 안전 채움 — 일반 크기(16px) 텍스트 버튼용 (DD-6 정책, P3 신설) | |||
| — | |||
| — | |||
| 소형 텍스트용 Primary — bg·surface·surface-selected·primary-subtle 표면에서 AA 보장(매트릭스 고정). surface-muted 위 사용 금지 | |||
| — | |||
| — | |||
| — | |||
| — | |||
| nav 활성/선택 표면(블루 틴트) | |||
| 떠 있는 표면(그림자로 위계) | |||
| — | |||
| — | |||
| — | |||
| P2 의도 변경(시드 gray.400→500) — 400은 백색 입력면에서 2.54:1, placeholder도 텍스트(AA 대상). parity 예외 기록 | |||
| — | |||
| — | |||
| 옅은 구분선/보더 | |||
| — | |||
| — | |||
| P2 의도 변경(시드 blue.600→700) — 600은 모든 라이트 표면에서 4.39:1로 AA 미달. parity 예외 목록 기록 | |||
| P2 의도 변경(시드 blue.700→800) — link 승격에 따른 hover 단계 이동 | |||
| 상태 표시(아이콘/보더/배지) — 텍스트는 success-text 사용 | |||
| 상태 표시 전용 — 라이트 표면 텍스트/단독 아이콘 금지(대비 2.2:1), warning-text + 아이콘 병용 | |||
| 상태 표시 — 텍스트는 error-text 사용 | |||
| 상태 표시 — 텍스트는 info-text 사용 | |||
| 상태 텍스트 — AA 4.5:1 (P2 신설) | |||
| 상태 텍스트 — AA 4.5:1 (P2 신설) | |||
| 상태 텍스트 — AA 4.5:1 (P2 신설) | |||
| 상태 텍스트 — AA 4.5:1 (P2 신설) | |||
| 비활성 컨트롤 배경 (P1 신설) | |||
| 비활성 컨트롤 전경 (P1 신설) | |||
| 모달/드로어 스크림 (P1 신설) | |||
| 본문 검색어 하이라이트 배경 — ::highlight(wds-search) (P1 신설) | |||
| A1 신설 — 보조 키 컬러(브랜드 시안 인접). M3 base tone40(라이트) | |||
| A1 신설 — secondary 채움 위 잉크. M3 on-base tone100 | |||
| A1 신설 — 보조 컨테이너 표면. M3 container tone90 | |||
| A1 신설 — secondary-container 위 잉크. M3 on-container tone10 | |||
| A1 신설 — 3차 키 컬러(primary +60° 회전). M3 base tone40(라이트) | |||
| A1 신설 — tertiary 채움 위 잉크. M3 on-base tone100 | |||
| A1 신설 — 3차 컨테이너 표면. M3 container tone90 | |||
| A1 신설 — tertiary-container 위 잉크. M3 on-container tone10 | |||
| A1 신설 — primary 컨테이너 표면(기존 primary-subtle와 별개의 on- 페어 보유 컨테이너). M3 container 대응 | |||
| A1 신설 — primary-container 위 잉크. M3 on-container 대응 | |||
| A1 신설 — surface-container 5티어 최저(가장 밝음). M3 light tone100. 이 5티어가 surface 톤 SSOT(00 §3.2) — Track B 머티리얼 매핑은 5-4 결정 | |||
| A1 신설 — surface-container 티어 low. M3 light tone96 | |||
| A1 신설 — surface-container 티어 기본. M3 light tone94 | |||
| A1 신설 — surface-container 티어 high. M3 light tone92 | |||
| A1 신설 — surface-container 티어 최고. M3 light tone90 | |||
| A1 신설 — M3 outline(경계/구분 UI). surface 위 UI 3:1. M3 light tone50 | |||
| A1 신설 — M3 outline-variant(장식 디바이더). 비게이트(저대비 의도, border-subtle와 동급). M3 light tone80 | |||
| A1 신설 — 반전 표면(스낵바/토스트). M3 light tone20 | |||
| A1 신설 — inverse-surface 위 잉크. M3 light tone95 | |||
| A1 신설 — inverse-surface 위 primary 액센트(다크 primary tone). M3 light=tone80 | |||
| A1 신설 — 에러 컨테이너 표면(기존 error 표시색과 별개의 컨테이너 페어). M3 container tone90 | |||
| A1 신설 — error-container 위 잉크. M3 on-container tone10 | |||
| 서포팅 액센트 사용 토큰 — 태그/카테고리 칩 배경(violet). text와 AA 4.5:1(contrast.test 게이트). UI 본문 색 아님(절제) | |||
| tag-violet-bg 위 라벨/아이콘 잉크 — AA 4.5:1 | |||
| 솔리드 채움(아바타/도트/카테고리 점) — 그래픽 요소. 본문 텍스트 배경 아님(대형 텍스트만) | |||
| 서포팅 액센트 사용 토큰 — 태그/카테고리 칩 배경(teal). text와 AA 4.5:1 | |||
| tag-teal-bg 위 라벨/아이콘 잉크 — AA 4.5:1 | |||
| 솔리드 채움(아바타/도트) — 그래픽 요소 | |||
| 서포팅 액센트 사용 토큰 — 태그/카테고리 칩 배경(magenta). text와 AA 4.5:1 | |||
| tag-magenta-bg 위 라벨/아이콘 잉크 — AA 4.5:1 | |||
| 솔리드 채움(아바타/도트) — 그래픽 요소 | |||
| 서포팅 액센트 사용 토큰 — 태그/카테고리 칩 배경(orange). Warning(amber)과 용도 구분: 장식·카테고리. text와 AA 4.5:1 | |||
| tag-orange-bg 위 라벨/아이콘 잉크 — AA 4.5:1 | |||
| 솔리드 채움(아바타/도트) — 그래픽 요소 | |||
| 서포팅 액센트 사용 토큰 — 태그/카테고리 칩 배경(cyan). text와 AA 4.5:1 | |||
| tag-cyan-bg 위 라벨/아이콘 잉크 — AA 4.5:1 | |||
| 솔리드 채움(아바타/도트) — 그래픽 요소 | |||
| 상태 컨테이너 — 연한 Success 표면(부드러운 알림/배지 바탕). M3 container tone90 | |||
| success-container 위 잉크. M3 on-container tone10 (AA) | |||
| 상태 컨테이너 — 연한 Warning 표면. M3 container tone90 | |||
| warning-container 위 잉크. M3 on-container tone10 (AA) | |||
| 상태 컨테이너 — 연한 Info 표면. M3 container tone90 | |||
| info-container 위 잉크. M3 on-container tone10 (AA) | |||
| 수식 생성(state-layer.ts) — text 8% over surface (M3 state layer, ADR-013 T3) | |||
| 수식 생성(state-layer.ts) — text 12% over surface (M3 state layer, ADR-013 T3) |
컴포넌트 토큰#
semantic만 참조합니다 — 테마는 참조 경로를 따라 자동 적용됩니다.
| 토큰 | 값 | 설명 |
|---|---|---|
| DD-6 해소: 버튼 라벨은 항상 일반 크기(≤16px) → 기본 채움이 AA 4.5:1을 보장해야 한다 (라이트 Blue700, 7.3:1) | ||
| — | ||
| — | ||
| — | ||
| — | ||
| — | ||
| — | ||
| — | ||
| — | ||
| — | ||
| — | ||
| — | ||
| — | ||
| — | ||
| — | ||
| — | ||
| — | ||
| — | ||
| — | ||
| — | ||
| — | ||
| P3 신설 — invalid 상태 보더 | ||
| — | ||
| — |
변경 절차#
토큰 변경은 영향 등급(Token Impact Matrix)에 따라 차등 승인됩니다 — Primitive 변경은 Governance Board 승인(High), 문서 수정은 Owner 승인(Low). 전체 절차는 MASTER_PLAN §12 참조.
P2 확정 기록#
| 항목 | 결정 | 근거 |
|---|---|---|
| Blue 700 표기 모호 | #1252B2 확정 | 시각 대조 — #125282(라벨)는 램프 hue 연속성 파괴 |
| Warning 표기 모호 | #F59E0B 확정 | 두 후보 시각 구분 불가 — 업계 표준값 채택 |
color.link | blue.600 → 700 승격 (의도 변경) | 600은 모든 라이트 표면에서 4.39:1 — AA 미달 |
color.on-primary(다크) | white → gray.900 반전 | 다크 primary(blue.500) 위 백색은 2.96:1 |
color.text-placeholder | 라이트 gray.400→500 / 다크 500→400 (의도 변경) | placeholder도 텍스트 — 시드값은 백색 입력면 2.54:1 |
| Button 600+백색 (DD-6) | large text 전용 정책 | 4.39:1 ≥ 3:1(large) — 일반 텍스트 버튼은 P3에서 strong 변형 |