Token Naming Conventions
이 문서가 답하는 질문: 토큰 이름을 어떻게 짜야 디자이너·개발자·도구가 모두 이해하는가? Tailwind의
colors.blue.500과 Panda의tokens.colors.primary는 같은 규약인가? 한 줄 답 (Pyramid Top): 토큰 이름은 **“category.type.item.state”**의 4단 hierarchical convention이 사실상 표준이다. Tailwind는 category.scale의 flat 컨벤션, Panda는 category.semantic의 alias 컨벤션 — 같은 토큰을 두 컨벤션으로 동시에 노출하는 것이 호환의 열쇠.
Why — 이름이 왜 그렇게 어려운가
토큰 이름은 다음 상호 모순적인 요구를 동시에 만족해야 한다.
| 요구 | 이름이 만족해야 할 것 | 충돌하는 다른 요구 |
|---|---|---|
| 디자이너가 알아본다 | 의미를 드러냄 (primary, danger) | 너무 추상적이면 코드에서 모름 |
| 개발자가 자동완성한다 | 일관된 prefix (color., space., font.) | 짧은 이름 선호 |
| 도구가 파싱한다 | 구분자 일관 (., -, /) | 도구마다 선호 다름 |
| Figma와 일치 | Figma 스타일 이름과 매칭 | Figma는 /, 코드는 . |
| 변경에 강하다 | 의미 기반 (bg.surface) | 의미는 자주 바뀜 |
| CSS variable로 변환 가능 | --color-primary 형식 | dot에서 dash로 변환 필요 |
이 충돌을 해결하는 합의된 패턴이 hierarchical naming + base.feature.modifier다.
How — 4가지 컨벤션 비교
컨벤션 1: Flat (Bootstrap, 초기 Tailwind)
$brand-primary
$gray-500
$btn-padding-y특징: prefix-suffix를 dash로. 한 평면. 단점: 그룹 표현 어려움, 자동완성 미흡.
컨벤션 2: Hierarchical dot-notation (DTCG, Style Dictionary, 현대 표준)
color.brand.primary
color.gray.500
button.padding.y특징: 점으로 그룹 중첩. JSON 구조와 1:1 매칭. 장점: 자동완성·grouping·alias가 자연.
컨벤션 3: BEM-like slash (Figma 기본)
color/brand/primary
button/padding/y특징: Figma Style 이름이 이 형식. 장점: Figma 호환. 단점: 파일 path와 시각 혼동.
컨벤션 4: kebab-case CSS variable (CSS 출력)
--color-brand-primary
--button-padding-y특징: CSS 변수의 유일한 합법 형식. dot/slash는 CSS에서 불법.
핵심: SSOT는 DTCG dot-notation이고, 출력 단계에서 각 플랫폼 컨벤션으로 변환한다.
What — base.feature.modifier 패턴
4단 hierarchical 구조
가장 널리 쓰이는 패턴 (Salesforce, Atlassian, Adobe Spectrum의 합집합):
{category}.{type}.{item}.{subitem}.{state}| 자리 | 의미 | 예시 |
|---|---|---|
| category | 토큰의 분야 | color, dim (dimension), font, dur (duration), shadow |
| type | 분야 내 그룹 | bg, fg, border (color일 때) / size, weight (font일 때) |
| item | 구체 역할 | surface, muted, primary, danger |
| subitem | 변형 | (생략 가능) subtle, bold, inverse |
| state | 상태 | (생략 가능) default, hover, active, disabled |
실전 예시
color.bg.surface ← page background
color.bg.surface.muted ← subtle surface
color.fg.default ← body text
color.fg.muted ← secondary text
color.fg.danger ← error text
color.border.default
color.border.focus
dim.space.4 ← spacing scale
dim.size.icon.sm
dim.radius.md
dim.radius.full
font.size.body ← typography role
font.size.heading.lg
font.weight.regular
font.weight.bold
dur.fast ← motion
dur.normal
button.bg.primary.default ← component (tier 3)
button.bg.primary.hover
button.bg.primary.disabled
button.padding.x
button.padding.y이름의 문법 권장사항
| 규칙 | 권장 | 비권장 |
|---|---|---|
| 일관된 구분자 | color.bg.surface (dot) | color-bg/surface (혼합) |
| 단수형 | color, space, font | colors, spaces, fonts (DTCG 표준) |
| 약어 일관 | dim 또는 dimension 둘 중 하나 | dim과 dimension 혼용 |
| 상태는 마지막 | button.bg.primary.hover | button.bg.hover.primary |
| 부정 회피 | fg.muted | fg.notBright |
| 의미 우선 | fg.danger | fg.red |
단수 vs 복수: DTCG는 단수형 권장. Style Dictionary와 Panda도 단수. 단, Tailwind는 colors, fontSize, spacing처럼 복수 — 호환 시 plural alias를 추가로 노출하는 트릭 필요.
What — Tailwind vs Panda 컨벤션 비교
Tailwind의 토큰 모델 (v3까지)
Tailwind는 flat primitive에 가까운 구조다.
// tailwind.config.ts
export default {
theme: {
colors: {
blue: {
400: '#60a5fa',
500: '#3b82f6',
600: '#2563eb',
},
gray: { ... },
},
spacing: { 0: '0', 1: '4px', 2: '8px', 4: '16px' },
fontSize: { sm: '14px', base: '16px', lg: '18px' },
}
}생성되는 유틸리티 클래스:
<div class="bg-blue-500 p-4 text-base">관찰:
- 키는 복수형 (
colors,spacing) - 값은 primitive 팔레트 위주 —
colors.primary같은 semantic이 공식 카탈로그에 없음 - semantic을 쓰려면 사용자가 직접
colors.primary: colors.blue[500]추가
Tailwind v4의 변화
v4부터 @theme CSS directive로 semantic이 1급 시민:
@theme {
--color-blue-500: #3b82f6;
--color-primary: var(--color-blue-500);
}→ bg-primary 클래스가 자동 생성. 처음으로 Tailwind에 semantic alias가 표준 경로로 들어왔다.
Panda CSS의 토큰 모델
Panda는 primitive와 semantic을 분리된 두 객체로 둔다.
// panda.config.ts
export default defineConfig({
theme: {
tokens: {
// Tier 1: primitive
colors: {
blue: {
400: { value: '#60a5fa' },
500: { value: '#3b82f6' },
600: { value: '#2563eb' },
},
},
},
semanticTokens: {
// Tier 2: semantic with mode switching
colors: {
primary: {
value: {
base: '{colors.blue.500}',
_dark: '{colors.blue.400}',
},
},
'bg.surface': {
value: {
base: '{colors.gray.50}',
_dark: '{colors.gray.950}',
},
},
},
},
}
})사용:
import { css } from '../styled-system/css'
<div className={css({ bg: 'primary', p: '4' })} />관찰:
tokens(primitive) vssemanticTokens(semantic) 분리- semantic에 내장된 mode 분기 (
base,_dark) {colors.blue.500}같은 alias 구문 — DTCG와 호환
비교 표
| 측면 | Tailwind v3 | Tailwind v4 | Panda CSS |
|---|---|---|---|
| 컨벤션 | flat (colors.blue.500) | CSS-first (--color-primary) | hierarchical (tokens.colors.primary) |
| 키 형식 | 복수형 (colors) | CSS var | 복수형 + value 객체 |
| primitive/semantic 분리 | 없음 (수동) | @theme로 가능 | tokens vs semanticTokens 강제 분리 |
| alias 구문 | JS 참조 (colors.blue[500]) | CSS var(--...) | {colors.blue.500} (DTCG-like) |
| mode (dark) | dark: variant | @theme 안에 :where(.dark) | semantic value에 _dark 키 |
| 자동 타입 | 약함 | 약함 | 강함 (TypeScript 자동 생성) |
What — DTCG → Tailwind/Panda 동시 출력
같은 DTCG JSON을 두 형식으로 변환하는 예. Style Dictionary 변환 결과:
DTCG 입력:
{
"color": {
"$type": "color",
"blue": {
"500": { "$value": "#3b82f6" }
},
"primary": {
"$value": "{color.blue.500}",
"$extensions": {
"modes": { "dark": "{color.blue.400}" }
}
}
}
}→ Tailwind preset:
// build/tailwind.preset.ts (Style Dictionary로 자동 생성)
export const tailwindPreset = {
theme: {
extend: {
colors: {
blue: {
500: '#3b82f6',
},
primary: 'rgb(var(--color-primary) / <alpha-value>)',
},
},
},
}→ Panda config:
// build/panda.tokens.ts
export const tokens = {
colors: {
blue: {
500: { value: '#3b82f6' },
},
},
}
export const semanticTokens = {
colors: {
primary: {
value: {
base: '{colors.blue.500}',
_dark: '{colors.blue.400}',
},
},
},
}→ CSS variables (공통 SSOT):
:root {
--color-blue-500: 59 130 246;
--color-primary: var(--color-blue-500);
}
[data-theme="dark"] {
--color-primary: var(--color-blue-400);
}Tailwind와 Panda가 둘 다 --color-primary를 가리키므로 런타임에 두 라이브러리가 같은 다크모드 토글에 반응한다. 자세한 빌드 방법은 05-style-dictionary-pipeline.
What-if — 네이밍에서 자주 깨지는 것
함정 1: 의미와 위치 혼동
// ❌ 위치 기반
color.header.bg
color.sidebar.bg
// ✅ 의미 기반
color.bg.surface
color.bg.elevated증상: 사이드바를 헤더로 옮기는 리팩토링에 토큰 이름까지 바꿔야 함. 대응: 어디 쓰이는지가 아니라 무엇을 뜻하는지로 이름.
함정 2: 색 이름을 의미로 사용
// ❌
color.red = #ef4444
button.bg.error = {color.red}
// ✅
color.red.500 = #ef4444 (primitive)
color.danger = {color.red.500} (semantic)
button.bg.error = {color.danger}증상: “danger를 주황으로 바꾸자”는 디자인 변경에 모든 토큰 이름을 바꿔야 함. 대응: primitive는 색 이름 OK, semantic은 역할 이름만.
함정 3: state를 중간에 끼움
// ❌
button.hover.bg.primary
// ✅
button.bg.primary.hover증상: 자동완성에서 button.hover를 친 순간 모든 hover 변형이 묶여 보임 — 역할보다 상태가 먼저 보임.
대응: state는 항상 마지막. 의미가 먼저, 상태가 나중.
함정 4: 단/복수 혼용
// ❌
color.primary
colors.gray.500
font.size.body
fontSizes.large증상: 도구가 color와 colors를 다른 그룹으로 인식. SSOT 깨짐.
대응: 한 컨벤션 강제. DTCG 단수형 → Tailwind 출력 시 복수형으로 build 단계에서 자동 변환.
함정 5: 너무 깊은 nesting
// ❌ 6단계
color.theme.brand.primary.button.hover
// ✅ 4단계 이내
button.bg.primary.hover증상: 자동완성이 무한 스크롤. 디자이너가 못 외움. 대응: 최대 4단계. category.type.item.state에 맞추기.
함정 6: Figma slash와 코드 dot의 비동기
Figma는 color/brand/primary, 코드는 color.brand.primary. 도구가 양방향 변환을 안 하면 수동 받아쓰기 발생.
대응: Tokens Studio 또는 Figma Variables → DTCG export 자동화. 사람이 두 번 입력하지 않게.
Insight — 왜 모든 디자인 시스템이 결국 비슷한 이름을 갖는가
대형 디자인 시스템 5개의 semantic color 토큰을 비교해보자.
| 시스템 | brand primary | error | page background |
|---|---|---|---|
| Adobe Spectrum | blue-900 | red-900 | gray-50 |
| Salesforce Lightning | color-brand | color-error | color-background |
| Atlassian | color.background.brand.bold | color.background.danger | color.background.neutral |
| Material 3 | primary | error | surface |
| Radix Themes | accent-9 | red-9 | gray-1 |
| 공통 흐름 | primary 또는 brand | danger 또는 error | surface 또는 bg |
서로 다른 회사들이 독립적으로 비슷한 이름에 수렴한 것은 우연이 아니다. 사용자 인터페이스에서 역할은 보편적이기 때문이다. 모든 앱에 brand 색, 위험 신호 색, 배경 색이 있다.
이름의 우주는 닫혀 있다. 그리고 그 닫힌 우주를 잘 정리한 합의가 category.type.item.state 4단 hierarchical convention이다.
흥미로운 반전: Tailwind의 flat 컨벤션도 사실은 이 규약의 축약형이다. bg-blue-500은 color.bg.blue.500의 prefix-축약이다. Tailwind v4가 --color-primary를 받아들이면서 두 진영이 같은 길로 수렴하고 있다.
요약
- 토큰 이름의 사실상 표준은
category.type.item.state4단 hierarchical convention. - 단수형, dot-notation, state는 마지막, 의미 기반 — DTCG 권장.
- Tailwind는 flat primitive 컨벤션, Panda는 primitive + semantic 분리 컨벤션.
- 같은 DTCG SSOT를 두 컨벤션으로 동시 출력하면 공존 가능.
- Figma slash / 코드 dot / CSS dash는 빌드 단계 변환으로 자동화한다.