🧩 Design System1. Tokens (DTCG·계층)Token Naming Conventions — base.feature.modifier, BEM-like, Tailwind vs Panda

Token Naming Conventions

이 문서가 답하는 질문: 토큰 이름을 어떻게 짜야 디자이너·개발자·도구가 모두 이해하는가? Tailwind의 colors.blue.500과 Panda의 tokens.colors.primary는 같은 규약인가? 한 줄 답 (Pyramid Top): 토큰 이름은 **“category.type.item.state”**의 4단 hierarchical convention이 사실상 표준이다. Tailwind는 category.scaleflat 컨벤션, Panda는 category.semanticalias 컨벤션 — 같은 토큰을 두 컨벤션으로 동시에 노출하는 것이 호환의 열쇠.


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, fontcolors, spaces, fonts (DTCG 표준)
약어 일관dim 또는 dimension 둘 중 하나dimdimension 혼용
상태는 마지막button.bg.primary.hoverbutton.bg.hover.primary
부정 회피fg.mutedfg.notBright
의미 우선fg.dangerfg.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는 primitivesemantic분리된 두 객체로 둔다.

// 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) vs semanticTokens (semantic) 분리
  • semantic에 내장된 mode 분기 (base, _dark)
  • {colors.blue.500} 같은 alias 구문 — DTCG와 호환

비교 표

측면Tailwind v3Tailwind v4Panda 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

증상: 도구가 colorcolors를 다른 그룹으로 인식. 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 primaryerrorpage background
Adobe Spectrumblue-900red-900gray-50
Salesforce Lightningcolor-brandcolor-errorcolor-background
Atlassiancolor.background.brand.boldcolor.background.dangercolor.background.neutral
Material 3primaryerrorsurface
Radix Themesaccent-9red-9gray-1
공통 흐름primary 또는 branddanger 또는 errorsurface 또는 bg

서로 다른 회사들이 독립적으로 비슷한 이름에 수렴한 것은 우연이 아니다. 사용자 인터페이스에서 역할은 보편적이기 때문이다. 모든 앱에 brand 색, 위험 신호 색, 배경 색이 있다.

이름의 우주는 닫혀 있다. 그리고 그 닫힌 우주를 잘 정리한 합의가 category.type.item.state 4단 hierarchical convention이다.

흥미로운 반전: Tailwind의 flat 컨벤션도 사실은 이 규약의 축약형이다. bg-blue-500color.bg.blue.500prefix-축약이다. Tailwind v4가 --color-primary를 받아들이면서 두 진영이 같은 길로 수렴하고 있다.


요약

  • 토큰 이름의 사실상 표준은 category.type.item.state 4단 hierarchical convention.
  • 단수형, dot-notation, state는 마지막, 의미 기반 — DTCG 권장.
  • Tailwind는 flat primitive 컨벤션, Panda는 primitive + semantic 분리 컨벤션.
  • 같은 DTCG SSOT를 두 컨벤션으로 동시 출력하면 공존 가능.
  • Figma slash / 코드 dot / CSS dash는 빌드 단계 변환으로 자동화한다.