이 글은 누구를 위한 것인가
- 브랜드 컬러 하나를 받아서 디자인 시스템 전체 팔레트를 만들어야 하는 디자이너
- 라이트/다크 모드 색상 토큰을 어떻게 설계해야 할지 모르는 팀
- Figma 변수와 CSS 변수를 연결하려는 개발자·디자이너
들어가며
브랜드 컬러가 #FF4B2B 하나 있다고 디자인 시스템이 완성되지 않는다. Primary 500부터 50까지의 스케일, 다크 모드 대응, 시맨틱 토큰(배경·텍스트·테두리)으로 확장해야 한다.
이 글은 bluefoxdev.kr의 디자인 시스템 색상 설계 를 참고하여 작성했습니다.
1. HSL 기반 색상 스케일 생성
[Primary 컬러 스케일 생성 방법]
브랜드 컬러: #FF4B2B (HSL: 11, 100%, 58%)
스케일 생성 규칙:
50: H=11, S=100%, L=96% (가장 밝음)
100: H=11, S=100%, L=90%
200: H=11, S=100%, L=80%
300: H=11, S=100%, L=70%
400: H=11, S=100%, L=64%
500: H=11, S=100%, L=58% ← 브랜드 컬러
600: H=11, S=100%, L=48%
700: H=11, S=100%, L=38%
800: H=11, S=100%, L=28%
900: H=11, S=100%, L=18%
950: H=11, S=100%, L=10% (가장 어두움)
[Gray 스케일 (중성색)]
브랜드 색상에 살짝 Hue를 가미 (따뜻한 브랜드 → 따뜻한 회색)
50: H=20, S=10%, L=98%
100: H=20, S=10%, L=95%
...
900: H=20, S=10%, L=10%
2. 시맨틱 토큰 설계
/* Design Tokens: Semantic Layer */
:root {
/* Primary Scale */
--color-primary-50: hsl(11, 100%, 96%);
--color-primary-100: hsl(11, 100%, 90%);
--color-primary-500: hsl(11, 100%, 58%); /* 브랜드 컬러 */
--color-primary-600: hsl(11, 100%, 48%);
--color-primary-700: hsl(11, 100%, 38%);
/* Semantic Colors - Light Mode */
--color-background-default: #ffffff;
--color-background-subtle: var(--color-gray-50);
--color-background-muted: var(--color-gray-100);
--color-text-default: var(--color-gray-900);
--color-text-secondary: var(--color-gray-600);
--color-text-disabled: var(--color-gray-400);
--color-border-default: var(--color-gray-200);
--color-border-strong: var(--color-gray-400);
--color-interactive-primary: var(--color-primary-500);
--color-interactive-primary-hover: var(--color-primary-600);
--color-interactive-primary-active: var(--color-primary-700);
/* Status Colors */
--color-success-subtle: hsl(142, 76%, 95%);
--color-success-default: hsl(142, 76%, 36%);
--color-danger-subtle: hsl(0, 86%, 97%);
--color-danger-default: hsl(0, 86%, 56%);
--color-warning-subtle: hsl(38, 100%, 95%);
--color-warning-default: hsl(38, 100%, 50%);
}
/* Dark Mode Override */
[data-theme="dark"] {
--color-background-default: hsl(20, 10%, 8%);
--color-background-subtle: hsl(20, 10%, 12%);
--color-background-muted: hsl(20, 10%, 16%);
--color-text-default: hsl(20, 10%, 95%);
--color-text-secondary: hsl(20, 10%, 70%);
--color-text-disabled: hsl(20, 10%, 45%);
--color-border-default: hsl(20, 10%, 22%);
/* Primary는 다크모드에서 밝게 조정 */
--color-interactive-primary: var(--color-primary-400);
--color-interactive-primary-hover: var(--color-primary-300);
}
3. Figma 변수와 CSS 변수 매핑
// Design Token을 JSON으로 내보내기 (Style Dictionary 형식)
const colorTokens = {
color: {
background: {
default: { value: "{color.white}", type: "color" },
subtle: { value: "{color.gray.50}", type: "color" },
},
text: {
default: { value: "{color.gray.900}", type: "color" },
secondary: { value: "{color.gray.600}", type: "color" },
},
interactive: {
primary: { value: "{color.primary.500}", type: "color" },
primaryHover: { value: "{color.primary.600}", type: "color" },
},
},
};
// Style Dictionary 설정
// 위 JSON → CSS 변수, iOS 색상, Android 색상 자동 생성
const StyleDictionary = require("style-dictionary");
StyleDictionary.extend({
source: ["tokens/**/*.json"],
platforms: {
css: {
transformGroup: "css",
files: [{
destination: "variables.css",
format: "css/variables",
}],
},
},
}).buildAllPlatforms();
마무리
색상 토큰 설계의 핵심은 레이어다: 원시 값(Primary-500) → 시맨틱 토큰(interactive.primary) → 컴포넌트 토큰(button.background). 컴포넌트가 원시 값을 직접 쓰면 테마 변경 시 모든 컴포넌트를 수정해야 한다. 시맨틱 토큰을 중간에 두면 다크 모드 전환이 토큰 값만 바꾸는 것으로 해결된다.