Dark ModeP2 본문

다크 모드

다크 테마의 설계 원칙과 시멘틱 오버라이드 규칙. 모든 텍스트 페어의 WCAG AA는 자동 테스트가 보증합니다.

마지막 업데이트 2026-06-12

설계 원칙#

다크는 "색 반전"이 아니라 시멘틱 재설계입니다. P2에서 확정했습니다.

  • semantic만 오버라이드 — primitive 램프는 두 테마가 공유합니다 (구조: 시드 B / 값: 시드 A 램프)
  • [data-theme='dark'] 가 전환 단위 — 중첩 가능(문서 안의 데모 패널이 반대 테마를 강제할 수 있음)
  • color-scheme 선언으로 네이티브 폼 컨트롤·스크롤바도 함께 전환됩니다
  • Flutter는 같은 SSOT의 WizColors.dark를 사용합니다

다크 전환 규칙 4가지#

① 인터랙션은 밝아진다#

라이트에서 hover는 어두워지고(600→700), 다크에서는 밝아집니다(500→400). Primary 자체도 한 단계 밝은 blue.500을 씁니다 — 600은 다크 표면에서 침침합니다.

② Primary 채움 위 잉크는 반전된다#

다크의 primary(blue.500) 위 백색 텍스트는 2.96:1로 읽기 어렵습니다. on-primary는 다크에서 gray.900(다크 잉크)으로 반전됩니다 — Material 다크 관례와 동일.

라이트

Primary 버튼

on-primary 반전 — 같은 토큰, 테마별 다른 잉크

③ 상태색은 -400 램프로 갈아탄다#

원색(500/600)은 다크 표면에서 대비가 부족합니다. 다크의 상태 표시·텍스트는 모두 -400 틴트를 사용합니다 (green-400 · amber-400 · red-400 · sky-400).

④ 그림자 대신 보더와 표면 위계#

다크에서 그림자는 거의 보이지 않습니다. 떠 있는 표면은 올라올수록 밝아져 위계를 만듭니다(M3 다크 규칙). surface-raised는 다크에서 neutral-tonal.t22surface(gray.800 ≈ 톤 16)보다 한 단계 밝은 톤으로, ADR-013 이전의 손으로 끼운 gray.750 중간 단계를 톤 수학 파생이 대체했습니다. 그림자 토큰은 두 테마 공통(core)으로 유지됩니다.

서피스 위계#

표면 토큰은 톤(neutral L*)이 올라가며 위계를 만듭니다. 다크는 라이트의 역방향 — 배경이 가장 어둡고, 올라온 표면일수록 밝습니다.

역할라이트다크다크 톤
bg (페이지)gray.50gray.900≈ t8
surface (콘텐츠)whitegray.800≈ t16
surface-raised (떠 있음)white + 그림자neutral-tonal.t22t22
surface-mutedgray.100gray.700≈ t27
surface-selectedblue.50blue.900
다크 표면 위계 — 같은 neutral 램프 위 톤 좌표
0
4
6
10
12
17
20
22
24
30
40
50
60
70
80
87
90
92
94
95
96
98
99
100
  • t8bg
  • t16surface
  • t22raised
  • t27muted

톤 번호 = CIELAB L*(지각 명도, D65) — 0 검정 → 100 흰색. 같은 번호는 모든 hue에서 같은 명도라 대비가 톤 차이로 결정됩니다.

상태 레이어 (hover · pressed)#

surface-hover·surface-pressed는 더 이상 손으로 고른 gray 단계가 아닙니다. M3 state layer 수식 — 콘텐츠 색(text)을 표면 위에 **hover 8% · pressed 12%**로 합성 — 으로 scripts/build-tokens/src/state-layer.ts가 빌드 타임에 생성합니다. 테마의 text/surface만 정하면 양 상태가 자동 파생되고, "다크는 밝아진다"가 수식에서 공짜로 나옵니다(다크 잉크는 밝은 gray.50이라 어두운 표면을 밝히는 방향).

라이트
surfacehoverpressedraised
surface → hover → pressed — 같은 수식, 테마별 자동 방향

AA 자동 검증#

scripts/build-tokens/test/contrast.test.ts가 텍스트/UI 페어 매트릭스를 양 테마에서 매 빌드 검증합니다 — 텍스트 4.5:1, 비텍스트 UI 3:1. *-text·text·text-muted 계열은 명명 규약 기반 자동 페어라 신규 토큰도 등록 없이 보호됩니다. 토큰 값을 바꾸면 대비 미달이 빌드에서 즉시 실패합니다.

의도된 예외(사용 규칙으로 관리 — 게이트 비대상):

  • 상태 원색(success/warning/error/info)은 표시 전용 — 텍스트는 *-text 토큰만. 특히 라이트 warning(amber.500)은 비텍스트 3:1도 미달(2.15) — 단색 아이콘/보더 단독 사용 금지, 항상 형태·라벨과 중복 표현
  • disabled-* 페어 — WCAG 1.4.3/1.4.11 inactive 면제(의도적 비대상)
  • text-subtle은 bg/surface 전용 — muted 표면에는 text-muted 사용
  • Primary 600 + 백색은 large text(≥24px 또는 ≥18.66px bold) 전용 (DD-6 정책)

시멘틱 토큰 — 양 테마 값#

토큰라이트다크설명
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)

Theme Architecture (예고)#

테마는 4계층으로 확장됩니다 — Foundation → WIZ Default → Customer → Enterprise. semantic 오버라이드 계층으로 실현하며(지금의 light/dark와 동일 메커니즘), MVP 범위 밖으로 구조만 보장합니다. 상세는 MASTER_PLAN §5-④.