Skip to content

라이트 · 다크 테마와 표면 맥락 ​

같은 시멘틱 토큰이 테마(라이트 · 다크)와 표면 맥락(기본 · 카드 · 브랜드 · 반전)에 따라 어떤 조합이 되는지, 그리고 어떤 토큰 · 클래스를 골라야 하는지 찾아보세요.

토큰 · Web / Mobile · 2026-10-13 신설 · 라이트는 전환 전후 픽셀 동일(Playwright 16장 0 차이)

규칙 (강제) · 코딩 에이전트용
  • 팔레트(--color-{hue}-{step})는 테마 불변이다. 다크는 시멘틱 토큰이 팔레트의 다른 단계를 가리키는 것이고, 어떤 테마·맥락 블록에서도 팔레트를 재정의하지 않는다(뉴트럴 반전 금지). 다크 전용 hex 도 없다.
  • 색은 시멘틱으로 고른다 — 원시 팔레트 유틸은 글자·면·경계에 쓰지 않는다. 글자 text-label-* · 면 bg-bg-* / .ds-surface--* · 경계 border-line-* · 상태 .ds-notice / .ds-label(상태 쌍 토큰) · 버튼 역할 클래스. bg-white · text-neutral-900 · bg-orange-500 같은 원시 유틸은 테마를 모른다 — 새 코드 금지, 기존은 파일을 건드릴 때만 이관.
  • 면이 있는 토큰은 짝으로 쓴다. action-* 는 -fg(+ -border), status-* 는 -bg · -fg · -border · -icon. 잉크 하나(text-status-success)를 본문 글자로 쓰지 않는다(흰 배경 2.99).
  • 표면 맥락은 컨테이너에 명시한다. 카드 .ds-surface--card · 브랜드 띠 .ds-surface--brand · 반전 띠 .ds-surface--inverse. 클래스가 면을 칠하고 안쪽 토큰을 재정의하므로 안의 버튼·글자·경계에 색을 지정하지 않는다. 맥락은 중첩하지 않는다. 「카드 위라서 버튼 색을 바꾸는」 화면별 지정은 금지 — 맥락 클래스가 그 일을 한다.
  • 버튼은 역할 클래스. ds-btn--primary · --primary-soft · --strong · --muted · --tertiary · --normal(line) · --danger · --danger-soft. 외형 이름(--dark · --white-bk · --white-or · --lightor · --red · --lightred)은 같은 규칙을 공유하는 별칭이라 위반이 아니지만 새 코드엔 쓰지 않는다. --white-or 대신 .ds-surface--brand 안의 --primary.
  • 테마 전환 UI 는 hereby Works(직원 앱)에만 둔다(2026-10-19). 관리자 앱 등 다른 앱은 다크 스타일을 대응해도 사용자용 테마 선택을 만들지 않는다.
  • 테마는 옵트인. 앱 main.ts 가 첫 페인트 전에 applyThemePreference(readStoredThemePreference())(@hereby/components)를 부를 때만 <html data-theme="dark"> 가 달린다. prefers-color-scheme 미디어쿼리를 CSS 에 쓰지 않는다. system 은 JS 가 푼다. 차트처럼 CSS 변수를 못 읽는 그림은 hereby:theme-change 를 듣고 다시 그린다.
  • 반투명은 합성 결과로 판정한다. 유리·딤·hover tint 의 합격은 렌더된 최종 색으로 잰다(이 페이지의 수치가 그것). 계산값으로 대신하지 않고, 글자에 반투명 색을 새로 만들지 않는다(브랜드 면 위 보조 글자는 불투명 orange-100).
  • 자리별 전용 토큰을 쓴다(2026-10-19 다크 점검). 늘 어두운 면(LNB · 워크스페이스 전환 · *-dark 배지) 위 흰 글자 · 선 · 틴트는 --ds-ink-white-*(정적 흰색) — --ds-glass-* 는 모바일 유리라 다크에서 어두워진다. 목록 묶음 사이 두꺼운 띠(border-t-8)는 border-line-section — line-* 는 얇은 선용이라 다크에서 밝은 띠가 된다. 이니셜 아바타 · 아이콘 썸네일 · 강조 칸의 연한 주황 면은 bg-bg-brand-tint / -soft.
  • 간격은 gap 으로 준다 — <p> · 입력칸 사이 space-y/mt-* 는 조용히 사라진다. 레이어 밖 p { margin: 0 } 과 DS 입력칸 기본 규칙(margin: 0)이 Tailwind 여백 유틸을 이긴다. 클래스로 여백을 주는 제목 · 설명은 div, 입력칸이 끼는 묶음은 flex flex-col gap-*.
  • 라이트를 바꾸지 않는다. 구조 전환(A)은 픽셀 동일이 전제였고 지금도 그렇다. 라이트 값을 바꾸는 항목(notice info 파랑 · 기존 대비 미달)은 별도 승인(설계 문서 §10).
  • 정본: packages/static/styles/design-system.css 「3. @theme static — semantic color aliases」 · 「테마 — 다크」 · 「표면 맥락」 블록. JS: packages/components/src/theme.ts. 설계: docs/superpowers/specs/2026-10-12-color-theme-token-design.md.

Playground — 테마 × 표면 맥락 ​

옵션을 바꾸면 미리보기와 코드가 함께 바뀌어요.
미리보기 테마
라이트 · 카드
직원 앱 도입

체크리스트 2/7 단계 · 첫 로그인 0%

아직 초대하지 않은 직원이 9명 있어요.
다크 · 카드
직원 앱 도입

체크리스트 2/7 단계 · 첫 로그인 0%

아직 초대하지 않은 직원이 9명 있어요.
왼쪽과 오른쪽은 완전히 같은 마크업이에요. 다른 것은 바깥의 data-theme과 컨테이너의 ds-surface--* 클래스뿐입니다. 버튼은 --color-action-*, 글자는 --color-label-*, 경계는 --color-line-* 토큰을 읽고, 맥락 클래스가 그 토큰을 재정의해요. 브랜드 면은 양 테마에서 주황을 유지하고(브랜드 불변), 반전 면은 라이트에서 어둡고 다크에서 밝아요.
<!-- 테마는 앱이 <html data-theme="dark"> 를 달 때만 바뀐다(옵트인). 맥락은 컨테이너의 클래스 하나. -->
<section class="ds-surface--card p-6 rounded-8">
  <div class="text-b3 font-semibold text-label-normal">직원 앱 도입</div>
  <p class="text-b5 text-label-assistive">체크리스트 2/7 단계 · 첫 로그인 0%</p>
  <div class="ds-notice ds-notice--warning mt-3">
    <span class="ds-ico ds-notice__icon" aria-hidden="true" x-html="$icon('ic-warning')"></span>
    <span class="flex-1">아직 초대하지 않은 직원이 9명 있어요.</span>
  </div>
  <div class="mt-4 flex gap-2">
    <button class="ds-btn ds-btn--solid ds-btn--tertiary ds-btn--md">나중에</button>
    <button class="ds-btn ds-btn--line ds-btn--normal ds-btn--md">자세히</button>
    <button class="ds-btn ds-btn--solid ds-btn--primary ds-btn--md">이어서 하기</button>
  </div>
</section>

무엇이 바뀌었나 ​

전후
@theme inline 이라 .text-label-normal{color:var(--color-neutral-900)} 로 박혀 시멘틱을 바꿔도 유틸리티에 안 닿음@theme static — 유틸리티가 var(--color-label-normal) 을 참조하고, [data-theme="dark"] 가 그 변수를 바꾼다
다크·테마 장치 없음[data-theme="dark"] 블록 하나(시멘틱 값만) + theme.ts 옵트인
버튼 색이 겉모양 이름(dark · white-bk)이고 팔레트 직접 참조역할 토큰 --color-action-* + 역할 클래스, 외형 이름은 별칭
상태색이 잉크 하나잉크 · 틴트 면 · 틴트 위 글자 · 경계 · 아이콘 쌍
「카드 위인지」를 화면이 알아서 white-or 등을 골라 씀컨테이너의 .ds-surface--card/--brand/--inverse 가 안쪽 토큰을 재정의
tokens.css 가 팔레트·시멘틱 113개를 다시 선언(KEEP IN SYNC)삭제 — @theme static 이 :root 에 전부 내보냄
입력칸 #fff · 스위치 손잡이 #ffffff · 라벨 rgb(193 193 193/…) · 포커스 링 …, white) 등 정본 안 하드코딩전부 토큰(값 동일) — 남은 literal 은 토큰 정의 안의 것뿐

계층과 명명 ​

① 팔레트      --color-{hue}-{step}                     @theme static   테마·맥락 불변
② 파운데이션  (두지 않음 — 팔레트가 이미 역할 중립)
③ 시멘틱      --color-{group}-{role}[-{part}]           @theme static   테마가 값을 바꾸고 맥락 클래스가 재정의
④ 컴포넌트    --ds-{component}-{part}                  :root           ③을 참조만 한다
그룹뜻기본값의 주인짝 접미사
bg표면면— (글자는 label)
label글자·아이콘 잉크잉크—
line경계잉크—
action버튼·칩 채움면-fg · -border · -soft · -soft-fg
status상태 의미색잉크-bg · -fg · -border · -icon
primary브랜드면-fg
static테마 불변 흑백—--color-static-white · -black

요청서의 text / surface / border 는 각각 label / bg / line 이에요 — 이미 1,768곳이 쓰는 어휘라 개명하지 않았습니다.

테마 켜기 ​

ts
// apps/<app>/src/main.ts — 첫 페인트 전, Alpine.start() 전
import { applyThemePreference, readStoredThemePreference } from "@hereby/components";
applyThemePreference(readStoredThemePreference()); // 기본 "light" — 부르지 않는 앱은 영원히 라이트
  • 선택값: "light" | "dark" | "system", localStorage hereby.theme(기기별). 설정 화면은 persistThemePreference 를 따로 부른다. 사용자에게 테마 전환 기능을 주는 앱은 hereby Works(직원 앱) 하나뿐이다(2026-10-19 결정) — 「더보기 → 화면 설정 → 화면 테마」. 관리자 앱은 다크 스타일을 대응해 두되 전환 기능을 주지 않는다: 부팅 적용은 사용자 키가 아니라 QA 전용 키를 읽어(readStoredThemePreference(ADMIN_THEME_QA_STORAGE_KEY)) 기본은 늘 라이트이고, 다크는 개발 · QA 때 localStorage hereby.theme.admin-qa = dark 를 넣어 확인한다. 관리자 앱은 hereby.theme 를 읽지 않는다 — 운영에서 관리자 앱(/)과 직원 앱(/employee-app/)은 같은 출처라 localStorage 를 함께 써서, 읽으면 Works 에서 고른 다크가 전환 기능이 없는 관리자 앱으로 샌다(2026-10-20). 다른 앱(hr-system · team-manager · 총무 등)도 전환 기능을 만들지 않는다.
  • 다크 값 요약 — 위계는 「높을수록 밝게」: 캔버스 900 < 기본·카드 800 < 모달 700. 잉크 400 · 틴트는 색을 표면(neutral-800)에 섞은 색 — 브랜드 선택 면 14% · 연한 주요 · 썸네일 22%, 상태 틴트 16% · 상태 경계 40%(2026-10-19~20, 전엔 900 · 700) · 틴트 위 글자 200 · 경계는 새 단계 neutral-450(3.10). orange 주요 버튼과 짙은 색 버튼(strong · danger · 의미색 솔리드)은 다크에서도 채움 그대로 + 흰 글자·아이콘(2026-10-13 결정). strong 은 짙은 면 위라 가는 경계(neutral-450)를 켠다.
  • 어떤 요소에도 data-theme="dark" 를 달 수 있어요(이 페이지의 나란히 비교가 그 방식). 앱에서는 <html> 에만.
  • 모든 컴포넌트 페이지에서 다크로 확인 — Playground 툴바의 「미리보기 테마」로 그 페이지의 모든 Playground(미리보기 · 모아보기)를 라이트 · 다크로 바꿔요. 미리보기 영역에 data-theme 을 다는 방식이라 앱의 다크와 같은 값입니다. 컴포넌트를 고치면 그 페이지에서 라이트 · 다크를 번갈아 눌러 확인합니다.
  • 문서 사이트 테마와 미리보기 테마 (2026-10-20) — 오른쪽 위 사이트 토글(VitePress)을 다크로 바꾸면 <html data-theme="dark"> 가 붙어 본문 · 표 · 데모까지 DS 다크 값으로 그려져요. 미리보기 테마는 사이트에 상대적이라(「같게」 · 「반대로」를 기억), 사이트를 바꾸면 라이트로 보던 미리보기는 다크로, 다크로 보던 것은 라이트로 뒤집혀요. 다크 페이지 안의 라이트 프레임은 docs 브리지의 [data-theme="light"] 블록(문서 전용, 생성물)이 토큰을 되돌려 그려요. 그래서 문서 데모 CSS 는 --vp-c-*(사이트 색)나 팔레트 면을 쓰지 않고 시멘틱 토큰을 씁니다 — 사이트 색은 미리보기 테마를 따르지 않아 반대 테마 미리보기에서 어긋나요.
  • 늘 어두운 면 위 흰 잉크는 --ds-ink-white-* — LNB 사이드바 · 워크스페이스 전환 · *-dark 배지처럼 테마와 무관하게 어두운 면에 올라가는 흰 글자 · 선 · 틴트는 --ds-glass-*(모바일 유리, 다크에서 어두워짐)가 아니라 --ds-ink-white-04 … -70(정적 흰색 기반)을 써요.

표면 맥락 ​

html
<section class="ds-surface--card rounded-8 p-6">     <!-- 카드: tertiary 버튼이 한 단계 내려가고 경계가 켜짐 -->
<div class="ds-surface--brand p-6">                  <!-- 브랜드 띠: primary 가 흰 채움 + orange-600 글자 -->
<nav class="ds-surface--inverse">                    <!-- 반전 띠: strong 이 면의 반대색 -->

맥락 클래스는 시멘틱 토큰만 재정의하고 컴포넌트 변수(--ds-btn-*)는 건드리지 않아요. 그래서 버튼뿐 아니라 링크 · 칩 · 아이콘 · 경계가 함께 맞습니다.

Playground — 사례 6 ​

옵션을 바꾸면 미리보기와 코드가 함께 바뀌어요.
미리보기 테마
① 카드 위에서 묻히는 버튼
원인
흰 버튼(white-bk)의 채움과 흰 카드의 면이 같은 색 — 경계가 없어 버튼이 사라진다(라이트 1.00).
맞는 수단
표면 맥락 분리 — 카드에 .ds-surface--card 를 달면 3차 버튼(tertiary)의 채움이 한 단계 내려가고 경계가 켜진다. 버튼 마크업은 그대로.
아닌 수단
테마 값 조정(라이트에도 있는 문제) · 새 variant(muted 가 이미 있음)
라이트
전
경계
후
경계
다크
전
경계
후
경계
수치는 이 페이지에 실제로 렌더된 색을 읽어 계산해요(반투명은 뒤 면까지 합성). 글자는 4.5, 경계·면은 3 기준이고 ✗ 는 미달입니다. 「전」은 지금 앱에 흔한 마크업, 「후」는 토큰·맥락 클래스로 바꾼 마크업이에요 — 두 줄의 차이가 곧 마이그레이션 규칙입니다.
<!-- 전 -->
<div class="bg-white p-6 rounded-8">
  <button class="ds-btn ds-btn--solid ds-btn--white-bk ds-btn--md">취소</button>
  <button class="ds-btn ds-btn--solid ds-btn--dark ds-btn--md">확인</button>
</div>

<!-- 후 -->
<div class="ds-surface--card p-6 rounded-8">
  <button class="ds-btn ds-btn--solid ds-btn--tertiary ds-btn--md">취소</button>
  <button class="ds-btn ds-btn--solid ds-btn--strong ds-btn--md">확인</button>
</div>

사례별 판단 ​

사례라이트다크맞는 수단아닌 수단
① 카드 위 묻히는 버튼흰 버튼 / 흰 카드 1.00같은 구조표면 맥락 분리 (.ds-surface--card)테마 값 조정 · 새 variant
② 브랜드 배경 위 동일 계열주황 위 주황동일표면 맥락 분리 (.ds-surface--brand)화면별 white-or
③ 다크에서 과한 밝기·채도—50 틴트·600 잉크가 뜸테마별 값 조정(시멘틱이 400 · 900 단계 참조)팔레트 반전
④ 경계가 사라지는 입력·보조 버튼테두리 1.261.73조합 변경(면·테두리·라인 버튼이 같은 토큰) + 필요 시 팔레트 단계 추가 (D7)입력칸 전용 색
⑤ 상태 표시잉크를 글자로 2.99600 잉크 미달variant 추가(잉크/bg/fg/border/icon 쌍) + 아이콘잉크 하나 겸용
⑥ 반투명유리 위 글자 가변흰 유리가 밝은 막측정 후 베이스 조정(--color-bg-glass)계산값으로 합격

Playground — 버튼 × 표면 맥락 × 상태 × 테마 ​

옵션을 바꾸면 미리보기와 코드가 함께 바뀌어요.
미리보기 테마
기본 글자 · 경계 ✗hover 글자 · 경계 ✗pressed 글자 · 경계 ✗focus 글자 · 경계 ✗disabled 글자 · 경계 ✗
한 역할을 다섯 상태로 펼쳐 보여요. hover · pressed · focus 는 DS 규칙과 같은 오버레이(20% · 10% · 30%)를 강제 클래스로 고정한 것이고, 수치는 렌더된 글자색과 오버레이를 합성한 최종 면으로 계산해요. 비활성은 WCAG 1.4.3 예외라 판정에서 뺍니다. 전 조합은 「모아보기」에.
<div class="ds-surface--card p-6 rounded-8">
  <button class="ds-btn ds-btn--solid ds-btn--primary ds-btn--md">저장</button>
</div>

읽는 법 ​

  • 수치는 렌더된 색으로 계산해요. 글자 = computed color, 면 = background-color 에 hover/pressed 의 inset 오버레이를 합성한 최종 색, 경계 = 면 vs 뒤 면과 테두리 vs 뒤 면 중 큰 값.
  • 기준: 글자 4.5 · 경계 3. 비활성은 WCAG 1.4.3 예외라 제외. hover · pressed · focus 는 DS 규칙과 같은 선언의 강제 클래스로 고정했어요(마우스 없이 측정).
  • 미달로 남는 것(결정으로 수용): primary 흰 글자 / orange-500 3.18(양 테마 — 브랜드 색 유지) · danger 흰 글자 / red-600 4.33 · tertiary 의 경계(기본 면에서 1.00~1.26 — 카드 맥락에서는 경계가 켜짐). 라인 버튼 경계는 neutral-450 으로 4.99(라이트) · 3.10(다크). 수치를 숨기지 않고 그대로 둡니다.

Playground — 대표 화면 라이트 · 다크 ​

옵션을 바꾸면 미리보기와 코드가 함께 바뀌어요.
미리보기 테마
라이트
직원 관리
김민지재직N-07

내과 병동 · 간호사

박서연휴직N-07

외래 · 간호조무사

이지은퇴직N-07

원무과 · 행정

다크
직원 관리
김민지재직N-07

내과 병동 · 간호사

박서연휴직N-07

외래 · 간호조무사

이지은퇴직N-07

원무과 · 행정

네 화면 모두 화면별 색 지정이 하나도 없어요 — 카드는 ds-surface--card, 버튼은 역할 클래스, 상태는 notice·라벨 쌍 토큰, 모달 면은 --color-bg-elevated. 다크에서 모달 면(700)이 카드(800)보다 밝고, 페이지 캔버스(900)가 가장 어두운 「높을수록 밝게」 위계를 확인하세요.
<div class="bg-bg-alternative p-4">
  <div class="ds-filter-bar">…</div>
  <article class="ds-surface--card rounded-8 p-4">
    <div class="flex items-center gap-2">
      <span class="text-b4 font-semibold text-label-normal">김민지</span>
      <span class="ds-label ds-label--sm ds-label--green">재직</span>
    </div>
    <p class="text-b6 text-label-assistive">내과 병동 · 간호사</p>
    <div class="flex justify-end gap-2 mt-3">
      <button class="ds-btn ds-btn--solid ds-btn--tertiary ds-btn--sm">상세</button>
      <button class="ds-btn ds-btn--solid ds-btn--strong ds-btn--sm">승인</button>
    </div>
  </article>
</div>

확인할 것 ​

  • 카드 목록: 캔버스(bg-alternative) 위 카드(.ds-surface--card)가 다크에서 한 단계 밝고, tertiary 버튼에 경계가 생기며, 라벨 틴트가 900 단계로 가라앉아요.
  • 폼: 입력칸 면 = 카드 면, 테두리 = 라이트 line-normal(#e5e5e5) · 다크 line-field(neutral-500 #5e5e5e — 2026-10-20 450 에서 한 단계 내림), 포커스 = line-dark, 오류 = status-error. 체크·라디오·스위치의 켜진 채움은 label-normal(다크에서 밝은 회색)이라 글리프는 어두운 체크로 바뀝니다.
  • 상태 표시: info 는 파랑 쌍(blue-50/200/700 · 다크 900/700/200), warning 은 노랑 — 2026-10-13 부터 색으로 구분돼요. 바 notice · notice · 라벨 전부 같은 상태 쌍을 읽어요.
  • 모달: 모달 면(700) > 카드(800) > 캔버스(900). 푸터는 tertiary + strong, 크리티컬 컨펌은 primary-soft.
  • 모바일 (hereby Works): 앱 헤더 면 = --ds-app-header-surface(앱이 bg-alternative/bg-container 로 정함) · 유리 캡슐 = --color-bg-glass · 하단 내비 면 = --ds-bottom-nav-surface · 선택 바탕 = bg-sunken(다크 bg-hover) · 홈 카드 = .ds-surface--card · 할 일 칩 = primary-soft · 강조 숫자 = text-label-brand. 직원 앱은 셸(main.css)과 모든 화면(근무 · 메신저 · 결재 · 더보기 · 2depth · 신청서 · 로그인)의 면 · 글자 · 선 · 썸네일을 토큰으로 이관해 다크가 실제로 적용돼요(2026-10-19). 사람 이니셜 · 아이콘 썸네일 · 강조 칸의 연한 주황 면은 bg-brand-tint / bg-brand-tint-soft 를 써요. 보조 글자는 label-muted(다크 400) · 비활성 글자는 label-disable(다크 450) 이에요.

Playground — 토큰 표 ​

옵션을 바꾸면 미리보기와 코드가 함께 바뀌어요.
미리보기 테마
토큰역할사용 맥락라이트다크다크 매핑짝
--color-action-primary주요 채움ds-btn--primary
→
action-primary-fg
--color-action-primary-fg주요 글자ds-btn--primary
→
action-primary
--color-action-primary-soft연한 주요 채움ds-btn--primary-soft (= lightor)
→
action-primary-soft-fg
--color-action-strong중립 강조 채움ds-btn--strong (= dark) · 서브 탭 선택
→
action-strong-fg
--color-action-strong-fg중립 강조 글자ds-btn--strong — 다크에서도 흰색
→
action-strong
--color-action-strong-border중립 강조 경계다크에서 짙은 면 위 검정 버튼을 가르는 선(라이트 transparent)
→
action-strong
--color-action-muted회색 채움ds-btn--muted
→
action-muted-fg
--color-action-tertiary물러남 채움ds-btn--tertiary (= white-bk) · 취소·닫기
→
action-tertiary-fg · -border(맥락이 켬)
--color-action-outline-border라인 기본 경계ds-btn--line ds-btn--normal
→
action-outline-fg
--color-action-danger위험 채움ds-btn--danger (= red) — 다크에서도 red-600 + 흰 글자
→
action-danger-fg
--color-action-danger-soft연한 위험 면(= lightred)ds-btn--danger-soft — 다크 red-900
→
action-danger-soft-fg
--color-action-danger-soft-fg연한 위험 글자ds-btn--danger-soft 글자 — 다크 red-200
→
action-danger-soft
--color-action-assistive옅은 회색 채움ds-btn--assistive — 다크 500
→
action-assistive-fg
--color-action-strong-hover중립 강조 hoverds-btn--strong hover · 체크박스/라디오 체크 hover — 다크 200
→
action-strong-fg
--color-action-primary-soft-fg연한 주요 글자ds-btn--primary-soft 글자 — 다크 orange-300
→
action-primary-soft
--color-action-muted-fg회색 채움 글자ds-btn--muted 글자
→
action-muted
--color-action-assistive-fg옅은 회색 글자ds-btn--assistive 글자
→
action-assistive
--color-action-tertiary-fg물러남 글자ds-btn--tertiary 글자(= label-normal)
→
action-tertiary
--color-action-tertiary-border물러남 경계카드 맥락(.ds-surface--card)에서만 보임 — 기본 transparent
→
action-tertiary
--color-action-outline-fg라인 기본 글자ds-btn--line.ds-btn--normal 글자
→
bg-container
--color-action-danger-fg위험 글자ds-btn--danger 글자 — 흰색 불변
→
action-danger
--color-action-overlay-strongpressed 오버레이 잉크버튼 :active 오버레이
→
—
--color-action-disabled-fg비활성 글자disabled 버튼 글자 — 다크 450(10/19)
→
action-disabled
--color-action-green의미색 솔리드 채움(green · blue · purple · aquablue · yellow)ds-btn--solid ds-btn--green 등 — 테마 불변, 흰 글자(yellow 는 어두운 글자)
→
action-{hue}-fg
--color-action-blue의미색 솔리드 파랑ds-btn--blue — 테마 불변 + 흰 글자
→
static-white
--color-action-purple의미색 솔리드 보라ds-btn--purple — 테마 불변
→
static-white
--color-secondary-purple-bg보라 보조 면필수(부서) 대화방 썸네일 — 글자는 secondary-purple
→
secondary-purple
--color-action-aquablue의미색 솔리드 아쿠아ds-btn--aquablue — 테마 불변
→
static-white
--color-action-yellow의미색 솔리드 노랑ds-btn--yellow — 테마 불변, 글자 neutral-900
→
label-normal
--color-action-green-fg의미색 솔리드 글자green · blue · purple · aquablue 는 흰색, yellow 는 neutral-900 — 테마 불변
→
action-green
--color-action-blue-fg의미색 솔리드 파랑 글자흰색 불변
→
action-blue
--color-action-purple-fg의미색 솔리드 보라 글자흰색 불변
→
action-purple
--color-action-aquablue-fg의미색 솔리드 아쿠아 글자흰색 불변
→
action-aquablue
--color-action-yellow-fg의미색 솔리드 노랑 글자neutral-900 불변
→
action-yellow
--color-action-icon-muted아이콘 버튼 muted 잉크ds-btn--icon ds-btn--muted(알림벨 · 새로고침) — label-muted 보다 한 단계 진함
→
bg-normal
--color-action-disabled비활성 채움모든 :disabled 버튼
→
action-disabled-fg
--color-action-overlayhover/focus 오버레이 잉크모든 버튼(20% · 30%)
→
action-overlay-strong(pressed 10%)

팔레트 단계별 다크 매핑 (전체 토큰)

라이트에서 쓰는 팔레트 단계마다 다크에서 어느 단계로 가는지 모은 표예요. 1:1 표시는 그 단계가 다크에서 한 단계로만 가고 그 다크 단계가 다른 라이트 단계에서 오지 않는 것. 같은 라이트 단계가 두 줄 이상으로 갈라지면 역할에 따라 다르게 매핑된 것이고(흰색은 면·글자·반전에서 모두 다르다), 1:1 이 아니어도 잘못이 아니에요 — 뒤집기 금지 원칙의 결과입니다.

라이트 단계다크 단계쓰는 토큰1:1

이 그룹: 1:1 0 · 합침 0 · 역할별 0 · 불변 0. 「1:1」은 그 라이트 값이 늘 같은 다크 값으로 가고 그 다크 값은 거기서만 오는 토큰, 「합침」은 서로 다른 라이트 값이 한 다크 값으로 모이는 토큰(예: bg-normal 과 bg-container 가 다크에서 둘 다 neutral-800), 「역할별」은 같은 라이트 값이 역할에 따라 다른 다크 값으로 가는 토큰, 「불변」은 양 테마가 같은 토큰이에요.

값은 이 페이지에서 실제로 계산된 색이에요(토큰을 적용한 프로브 요소의 computed 값). 「표면 맥락」을 바꾸면 그 맥락 클래스가 재정의한 값으로 바뀝니다 — 예를 들어 브랜드 면에서 --color-action-primary가 흰색이 되는 것을 볼 수 있어요. 팔레트 토큰(--color-orange-500 등)은 테마가 바뀌어도 그대로라 표에 없습니다.
--color-action-primary: …; /* 다크 … */
--color-action-primary-fg: …; /* 다크 … */
--color-action-primary-soft: …; /* 다크 … */
--color-action-strong: …; /* 다크 … */
--color-action-strong-fg: …; /* 다크 … */
--color-action-strong-border: …; /* 다크 … */
--color-action-muted: …; /* 다크 … */
--color-action-tertiary: …; /* 다크 … */
--color-action-outline-border: …; /* 다크 … */
--color-action-danger: …; /* 다크 … */
--color-action-danger-soft: …; /* 다크 … */
--color-action-danger-soft-fg: …; /* 다크 … */
--color-action-assistive: …; /* 다크 … */
--color-action-strong-hover: …; /* 다크 … */
--color-action-primary-soft-fg: …; /* 다크 … */
--color-action-muted-fg: …; /* 다크 … */
--color-action-assistive-fg: …; /* 다크 … */
--color-action-tertiary-fg: …; /* 다크 … */
--color-action-tertiary-border: …; /* 다크 … */
--color-action-outline-fg: …; /* 다크 … */
--color-action-danger-fg: …; /* 다크 … */
--color-action-overlay-strong: …; /* 다크 … */
--color-action-disabled-fg: …; /* 다크 … */
--color-action-green: …; /* 다크 … */
--color-action-blue: …; /* 다크 … */
--color-action-purple: …; /* 다크 … */
--color-secondary-purple-bg: …; /* 다크 … */
--color-action-aquablue: …; /* 다크 … */
--color-action-yellow: …; /* 다크 … */
--color-action-green-fg: …; /* 다크 … */
--color-action-blue-fg: …; /* 다크 … */
--color-action-purple-fg: …; /* 다크 … */
--color-action-aquablue-fg: …; /* 다크 … */
--color-action-yellow-fg: …; /* 다크 … */
--color-action-icon-muted: …; /* 다크 … */
--color-action-disabled: …; /* 다크 … */
--color-action-overlay: …; /* 다크 … */

라이트 값은 바뀌지 않았다 ​

구조 전환(A 단계)은 값 참조만 바꿨으므로 라이트가 바뀌지 않아요 — 정본 CSS 를 바꾸기 전과 후를 Playwright 로 16장(관리자 5 · 직원 앱 3 · docs 8, 로그인 상태 포함) 찍어 픽셀 비교해 0 차이였습니다(Playwright 기본 임계: 픽셀당 YIQ 0.2 — 참조만 바꾼 A 단계는 값이 같아 임계와 무관하게 동일). 그 뒤 사용자 결정으로 라이트가 바뀐 것은 info notice 파랑 · 입력칸/셀렉트/라인 버튼 테두리(neutral-450) · muted 아이콘 버튼(500) · 직원 앱 hr-gray → neutral 이관(미세) 네 가지이고, 각각 diff 이미지로 그 영역만 바뀐 것을 확인한 뒤 기준선을 다시 떴습니다. --color-bg-elevated 의 라이트 값(#fdfdfd → #ffffff)은 쓰는 곳이 없어 화면 변화가 없습니다(결정 D4).

다크 값 — 점검 반영(2026-10-19) ​

직원 앱 41화면 · 관리자 앱 20화면 · 이 문서의 Playground 136개를 다크로 찍어 렌더된 색으로 대비를 계산하고 고친 값이에요. 라이트는 하나도 바뀌지 않았어요(관리자 앱 20화면 픽셀 비교). 위 토큰 표는 정본 CSS 를 직접 읽으므로 같은 값이 보여요. 결정 기록은 설계 문서 §13.

토큰라이트다크쓰는 곳 · 이유
--color-label-mutedneutral-400neutral-400 (전 500)보조 문구 · 날짜 · 사번 · 도움말 · 서브 탭 숫자. 4.74
--color-label-disableneutral-300neutral-450 (전 600)비활성 글자 · 구분점 · 메신저 시간. 3.10, 보조보다 옅음
--color-action-disabled-fglabel-disableneutral-450 (전 500)비활성 버튼 글자. 바탕 700 위 2.8
--color-primary-bgorange-50bg-brand-tint-soft (전 orange-900)선택 칸 · 선택 옵션 면 — 선택은 테두리 · 체크가 알림
--color-action-primary-softorange-50bg-brand-tint (전 orange-900)연한 주요 버튼(보기 · 작성 · 단체 채팅 · AI 추천)
--color-bg-brand-tintorange-100주황 500 22% + neutral-800이니셜 아바타 · 아이콘 썸네일 · 앱 헤더 아바타
--color-bg-brand-tint-softorange-50주황 500 14% + neutral-800강조 칸 · 옅은 주황 타일
--color-line-sectionneutral-100neutral-900목록 묶음 사이 두꺼운 띠 — 카드보다 가라앉음
--ds-ink-white-04 … -70흰색 n%흰색 n%(불변)LNB · 워크스페이스 전환 · *-dark 배지 위 흰 잉크
--color-status-{success,warning,error,info}-bg · --ds-secondary-{blue,purple}-bg50 단계상태색 500 16% + neutral-800 (전 900)안내 박스 · 바 notice · 상태 배지 · 출근 완료 안내(2026-10-20)
--color-status-*-border200 단계상태색 500 40% + neutral-800 (전 700)안내 박스 · 라인 배지 테두리

컴포넌트별 다크 값 ​

정본 CSS 에서 다크 동작이 바뀐 컴포넌트예요. 각 컴포넌트 페이지의 Playground 툴바 「미리보기 테마 → 다크」로 바로 볼 수 있어요.

컴포넌트무엇라이트다크
앱 헤더뒤로(<) 글자label-normal(#1b1b1b)label-normal(밝은 글자) — 전엔 hr-gray-800 고정이라 묻힘
〃아바타 원 · 이니셜bg-brand-tint · label-brand주황 22% 틴트 · orange-300
탭 바비활성 탭 글자 --ds-tab-inactive-fgneutral-300label-disable(450)
배지ds-label--gray 글자 --ds-label-fg-assistivelabel-assistivelabel-alternative
〃ds-label--gray-dark · --light-gray-dark흰 잉크(--ds-ink-white-*)같음(불변)
버튼라인 위험(--line.--danger) 글자secondary-red(red-600)red-400
〃비활성 글자label-disableneutral-450
〃연한 주요(--primary-soft / --lightor) 면orange-50bg-brand-tint
폼 컨트롤입력칸 hover 테두리 --ds-textfield-border-hoverline-strong(#c1c1c1)line-alternative(400)
필터 바제목 .ds-filter-bar__titlelabel-alternativeneutral-300
〃스크롤 화살표 · 검색 토글 면bg-containerneutral-800
LNB 사이드바메뉴 · 하위 메뉴 · 화살표 · 토글 글자/선흰 잉크 70 · 60 · 55 · 18같음(불변) — 전엔 다크에서 어두워져 사라짐
〃접힘 플라이아웃 면bg-elevatedneutral-700
워크스페이스 전환트리거 글자 · 테두리 · 틴트흰 잉크같음(불변) · 드롭다운 면 bg-elevated
페이지 헤더헤더 · 팀 선택 · 뒤로 · 새로고침 면bg-containerneutral-800
서브 탭여러 개 선택(isActive)—— (모양은 단일 선택과 같음)
목록 카드 .ds-list-card · 탭 바 화살표 · 소속 칩면bg-containerneutral-800
스켈레톤흰 면 · 회색 면(--on-sunken) · 떠 있는 면(--on-elevated) 위 자리표시--color-skeleton · -sunken · -elevated (neutral-75 · 100 · 75)600 40% + 800(≈#343434) · neutral-700 · 600 50% + 700(≈#3b3b3b) — 면보다 밝게, 차이 약 15 (10-21)
notice · 바 notice틴트 면 · 테두리 · 글자 · 아이콘status-*-bg/-border/-fg/-icon (50 · 200 · 700 · 500)상태색 500 16% · 40% + neutral-800 · 200 · 400 (10-20)
라벨 · 배지검정 라벨 칩 면 · 글자 · 흰 라벨 칩 면--ds-label-black-bg · -fg (label-normal · neutral-25) · --ds-label-white-bg(neutral-25)action-strong(neutral-900) · 흰색 + 가는 경계 · bg-elevated(neutral-700)
메신저 공용 · 직원 앱 상태 면연결 상태 띠 · 경고/완료 원 아이콘 · 기념일 타일 · 부서 방 썸네일상태색 쌍 status-*-bg / -fg / -icon · secondary-purple-bg상태색 500 16% 틴트 · 보라 16%
하단 내비면 · 윗선 · 분리형 캡슐 · 선택 유리 · 캡슐 테두리 · 그라데이션 끝bg-container · line-normal · --ds-bottom-nav-capsule-bg(bg-glass 66%) · --ds-bottom-nav-glow-bg(bg-glass 78%) · glass-55 · --ds-bottom-nav-surface(앱이 줌)neutral-800 · 450 · neutral-900 92%(화면보다 어둡게, 10-20) · neutral-700 92% · 흰 잉크 10% · 앱 값 — 고정 흰색을 넘기면 흰 띠(10-20 직원 앱 메신저)
대화 UI패널 · 입력줄 · 받은 말풍선 · 오류 말풍선bg-elevated · bg-container · bg-sunken · status-error-bgneutral-700 · 800 · 900 · red 16%
AI 어시스턴트이전 대화 목록 · 서랍 · 항목 hoverbg-alternative · bg-elevated · bg-hoverneutral-900 · 700 · 700
검색 입력 · 소속 칩칩 면 · 글자 · × · 행 hoverbg-container · label-body · label-disable · label-normal 4%neutral-800 · 100 · 450 · 4%(밝은 틴트)
페이지네이션버튼 글자 · hover · 현재label-alternative · bg-hover · bg-selected300 · 700 · 600 — ⚠ 앱별 복사본에 있음
모달카드 면(.ds-modal · .ds-mini-modal)bg-elevated #fffneutral-700 — 마크업 bg-white 는 다크에서 흰 면으로 남으므로 지운다
툴팁도움말 배지 테두리 · 글자 · hover 면line-strong · label-muted · bg-inverse300 · 400 · neutral-50(반전)
데이터 테이블목록 카드 · 비활성 카드 · 한 줄 셀 hoverbg-container · bg-alternative · bg-alternative800 · 900 · 900
모바일 대화검색 줄 이동 버튼 · 대화방 바탕bg-containerneutral-800
로그인 · 가입 · 비밀번호 폼 · 게이트 화면바깥 상자 · 아이콘 원 · 글자bg-container / bg-alternative · status-warning-bg · label-*neutral-800 / 900 · yellow 16% · 밝은 글자 — 저장된 테마를 따름(Works)
메신저 공용루트 면 · 탭 줄 · 날짜 구분선 · 아바타bg-container · line-alternative · bg-brand-tintneutral-800 · 400 · 주황 22%
직원 추가 · 수정 패널 · 권한 모달카테고리 내비 · 섹션 카드 · 모달 면bg-container / bg-alternative · bg-elevatedneutral-800 / 900 · 700 (관리자 앱 — QA 전용)
내 정보 폼일부 이관(25곳 보류 — 테마 옵트인 앱이 안 씀)——
근무표(Table 2)셀 면 · 주말 열 틴트 (admin main.css — 유일한 복사본)bg-container · bg-brand-tint-soft 50%neutral-800 · 주황 14% 50%

접근성 판정 — 계산값과 측정값 ​

구분무엇어디서
계산값불투명 쌍의 WCAG 비(팔레트 단계 고르기용)설계 문서 §6 · §9
측정값이 페이지가 렌더한 색으로 계산 — 반투명·오버레이·유리 포함「사례 6」 · 「버튼 매트릭스」
기준글자 4.5 · 큰 글자 3 · 경계/아이콘 3 · 비활성 제외—

Playground — 바꿀 것 찾기 ​

옵션을 바꾸면 미리보기와 코드가 함께 바뀌어요.
미리보기 테마
전bg-white (페이지·카드)
후ds-surface--card (카드) · bg-bg-normal (페이지 면)

bg-white 는 var(--color-white) 라 테마를 모른다(708곳). 카드는 맥락 클래스가 면과 안쪽 토큰을 함께 정한다.

라이트
전
카드 제목

bg-white · text-neutral-900

후
카드 제목

ds-surface--card · text-label-normal

다크
전
카드 제목

bg-white · text-neutral-900

후
카드 제목

ds-surface--card · text-label-normal

「전」은 테마를 모르는 마크업이라 다크 프레임에서도 흰색·검정 그대로예요. 「후」는 같은 자리에 토큰·맥락 클래스를 쓴 것이고, 라이트에서는 전과 같은 픽셀입니다(정본 CSS 전환 때 Playwright 로 0 차이 확인). 이관은 파일을 건드릴 때 그 파일 안에서만 — 일괄 스윕은 하지 않아요.
/* 전 */ bg-white (페이지·카드)
/* 후 */ ds-surface--card (카드) · bg-bg-normal (페이지 면)

계획 ​

이관은 파일을 건드릴 때 그 파일 안에서만 한다(일괄 스윕 금지 — 버튼·모달 규칙과 같은 원칙). 켜지 않은 앱은 바뀌지 않으므로 순서는 자유다.

단계대상규모메모
1docs 매트릭스 · admin 게이트 화면 · 드라이브(시멘틱 75%)파일럿D8
2bg-white → ds-surface--card / bg-bg-normal708곳(모달 177 은 CSS 가 면을 칠하므로 지우기만)카드면 맥락 클래스
3text-white → 맥락 안 text-label-normal / 역할 -fg99곳테마 불변이어야 하면 text-label-white
4원시 뉴트럴 유틸 → text-label-* · border-line-* · bg-bg-*약 6,975곳 중 뉴트럴 계열같은 값이라 라이트 무변화
5hr-* 팔레트 동결 · 직원 앱 이관 — 셸 CSS · 근무(홈) · 더보기 · 화면 설정(10-13, 216곳) + 결재 · 모달류 · 메신저 · 로그인 · 공용 파셜(10-19) 완료. 남은 것: 상태 배지 색(formatters.ts)직원 앱매핑: text-hr-gray-900/800→label-normal · 700→label-body · 600→label-alternative · 500→label-assistive · 400→label-muted · 300→label-disable · bg-hr-gray-50→bg-alternative · 100→bg-hover · 200→bg-selected · bg-neutral-75→bg-sunken · border-hr-gray-100→line-normal · 200→line-alternative · bg-white→bg-container · text-orange-600→label-brand · bg-orange-100→bg-brand-tint · bg-orange-50→bg-brand-tint-soft · text-black→label-black-strong
6차트 dsColor() → 런타임 getComputedStyle + hereby:theme-change경영 대시보드 47곳canvas 는 var() 불가
7네이티브 Light 고정 해제(android styles.xml · iOS UIUserInterfaceStyle)직원 앱다크 출시 PR 에서
8notice --info 파랑적용됨(2026-10-13)D5
9color.md 「고르는 순서」 시멘틱 1순위로문서이번에 반영

Playground — 보류 결정의 대안 ​

옵션을 바꾸면 미리보기와 코드가 함께 바뀌어요.
미리보기 테마
주요 버튼 — orange-500 + 흰 글자 유지 (3.18, 양 테마)

브랜드 색을 그대로 두고 다크에서도 흰 글자·아이콘으로 통일하기로 했다(짙은 색 버튼 공통 규칙). 대안 A·B 는 고르지 않았다.

현재(유지) · orange-500 + 흰 글자

3.18 — 글자 4.5 미달을 브랜드 유지로 수용

라이트
카드 위
다크
카드 위
A · orange-600 채움 (안 고름)

4.56 ✓ — 브랜드 색이 버튼에서 달라진다

라이트
카드 위
다크
카드 위
B · 어두운 글자 (안 고름)

5.36 ✓ — 「짙은 버튼은 흰 글자」 규칙과 어긋난다

라이트
카드 위
다크
카드 위
열 = 후보(맨 왼쪽 「현재」가 지금 정본), 열 안의 위가 라이트 · 아래가 다크. 수치는 렌더된 색으로 계산한 실측이고 글자 4.5 · 아이콘/경계 3 기준이에요. 「이전」·「안 고름」 열은 미리보기 안에서만 토큰을 덮어쓴 기록이라 정본에는 없습니다. 되돌리려면 「코드」 탭의 선언을 정본에 옮기면 돼요.
/* D6 · 라이트 기존 대비 미달 (결정 완료) — 주요 버튼 — orange-500 + 흰 글자 유지 (3.18, 양 테마) */
/* 「현재」 는 정본 그대로. 아래는 고르지 않은 대안·이전 값을 미리보기 안에서 덮어쓴 선언. */
/* A · orange-600 채움 (안 고름) */
:root {
  --color-action-primary: var(--color-orange-600);
}
[data-theme="dark"] {
  --color-action-primary: var(--color-orange-600);
}
/* B · 어두운 글자 (안 고름) */
:root {
  --color-action-primary-fg: var(--color-neutral-900);
}
[data-theme="dark"] {
  --color-action-primary-fg: var(--color-neutral-900);
}

무엇을 고르는가 ​

2026-10-13 에 전부 결정돼 정본에 반영됐어요(아래 표). Playground 의 「현재」 열이 지금 정본이고, 나머지 열은 고르지 않은 대안(또는 이전 값)을 미리보기 안에서만 덮어쓴 기록이에요 — 되돌리거나 다시 고를 때 참고합니다.

결정현재대안바뀌는 곳
D7 다크 경계 3:1neutral-600(1.73)적용 · A 새 단계 neutral-450 #707070(실측 3.10): 다크 line-normal · 카드 · strong 경계다크만
D6 주요 버튼 3.18orange-500 + 흰 글자유지 — 브랜드 색 그대로, 다크에서도 흰 글자(3.18 양 테마). 대안 A orange-600(4.56) · B 어두운 글자(5.36)는 안 고름—
D6 연한 주요 4.16orange-600 글자유지(대안 orange-700 5.92 안 고름)—
D6 상태 잉크 글자600 / 400 / 600유지 — 글자는 틴트 쌍(notice·라벨)으로—
D6 notice 글자700(3.93 · 4.42)유지 700—
D6 입력칸 테두리 1.26neutral-100적용 — 새 단계 neutral-450(흰 배경 4.99 · 다크 3.10)을 --color-line-field 로 입력칸·셀렉트·라인 버튼 경계에. 카드·구분선(line-normal)은 그대로. 2026-10-20 재결정: 라이트 입력칸 · 셀렉트(--ds-textfield-border) · 라인 버튼 경계(--color-action-outline-border)를 neutral-100 으로 되돌림 — 450 이 너무 진하게 읽혔고 입력칸은 hover(300)보다 진한 역전도 있었다. 다크 line-field 도 450 → 500(카드 면 위 3.10 → 2.36) — 너무 밝게 읽혔다. 3:1 기준은 이 두 컨트롤에서 내려놓았다입력칸 · 라인 버튼 — 라이트 시각 변화
D6 label-muted 3.23neutral-400유지 — 단 아이콘 버튼 muted 의 잉크만 --color-action-icon-muted(500 · 다크 400)로 진하게(적용). 2026-10-19 재결정: 다크 값만 500 → 400(container 위 2.36 → 4.7, 직원 앱 다크 점검에서 보조 문구 · 비활성 탭 · 날짜가 읽히지 않음). 라이트는 그대로아이콘 버튼 muted(알림벨 등) — 라이트 시각 변화
D5 info 파랑warning 과 같은 노랑적용 · A blue-50/200/700, 아이콘 500 · 다크 900/700/200, 아이콘 300(실측 8.04 · 9.16) — 10/19 노랑으로 되돌렸다가 같은 날 사용자 확인으로 파랑 유지notice info 189곳 · 바 notice info — 라이트 시각 변화