🧩 Design System1. Tokens (DTCG·계층)W3C DTCG Format — $value, $type, $description, $extensions, alias {ref}

W3C DTCG Format

이 문서가 답하는 질문: W3C Design Tokens Community Group 포맷의 정확한 문법은? $value, $type, $description, $extensions는 각각 어떤 역할이고 alias {ref} 구문은 어떻게 동작하는가. 한 줄 답 (Pyramid Top): DTCG는 **“토큰을 JSON 객체로 표현하되, 로시작하는메타키만예약어"로두는단순한규약이다.일반키는그룹이거나토큰이며,토큰은로 시작하는 메타 키만 예약어"**로 두는 단순한 규약이다. 일반 키는 *그룹*이거나 *토큰*이며, 토큰은 `value`를 가지면 토큰이다.


Why — 왜 또 다른 JSON 포맷이 필요했나

2014~2020년 사이, 디자인 토큰 포맷은 벤더마다 달랐다.

벤더 / 도구포맷 예시문제
Salesforce Theo{ "props": [{ "name": "COLOR_PRIMARY", "value": "#3b82f6" }] }배열 기반, 그룹 표현 어색
Style Dictionary (v3){ "color": { "primary": { "value": "#3b82f6" } } }value가 일반 키와 충돌 가능
Tokens Studio (Figma)자체 포맷, set 개념 추가도구 lock-in
Adobe Spectrum별도 YAML다른 도구가 못 읽음

문제: Figma에서 export한 JSON을 Style Dictionary가 못 읽고, Style Dictionary가 만든 JSON을 Tokens Studio가 못 읽는다. N × N 변환이 필요했다.

2021년 W3C Design Tokens Community Group(DTCG)이 결성된 동기는 단순했다: “하나의 JSON을 모두가 읽게 하자”. Figma, Salesforce, Adobe, Atlassian, Microsoft, Google 등 주요 벤더가 참여했다.


How — DTCG의 5가지 규칙

규칙 1: 일반 키는 그룹, $value를 가진 객체는 토큰

{
  "color": {                        // 그룹
    "primary": {                    // 토큰 (아래에 $value 있음)
      "$value": "#3b82f6",
      "$type": "color"
    },
    "brand": {                      // 그룹 (안에 또 토큰들)
      "blue": {
        "$value": "#3b82f6",
        "$type": "color"
      }
    }
  }
}

토큰 식별 규칙: “$value 키가 있으면 토큰, 없으면 그룹”. 그룹은 임의 깊이로 중첩 가능.

규칙 2: $로 시작하는 키만 예약어

예약 키의무위치의미
$value필수 (토큰일 때)토큰실제 값 또는 alias
$type강력 권장토큰 또는 그룹값의 타입
$description선택토큰 또는 그룹사람용 docs
$extensions선택토큰 또는 그룹벤더 확장
$deprecated선택토큰deprecated 마커

$가 없는 키는 항상 그룹 이름 또는 토큰 이름. 충돌 없음.

규칙 3: alias는 {path.to.token}

{
  "color": {
    "blue": {
      "500": { "$value": "#3b82f6", "$type": "color" }
    },
    "primary": {
      "$value": "{color.blue.500}",       // ← alias
      "$type": "color"
    }
  }
}

중괄호 안에 그룹 경로를 점(.)으로 잇는다. resolver는 재귀적으로 따라가 최종 값에 도달한다.

규칙 4: $type은 부모 그룹에서 상속

{
  "color": {
    "$type": "color",            // ← 그룹 레벨에 한 번
    "blue": {
      "500": { "$value": "#3b82f6" }    // $type 생략 가능 — 그룹에서 상속
    },
    "red": {
      "500": { "$value": "#ef4444" }
    }
  }
}

$type을 매 토큰에 쓰지 않아도 그룹 레벨에 한 번 적으면 모든 자식 토큰이 상속한다.

규칙 5: composite token은 $value가 객체

{
  "shadow": {
    "card": {
      "$type": "shadow",
      "$value": {
        "color": "{color.shadow.subtle}",
        "offsetX": "0px",
        "offsetY": "2px",
        "blur": "8px",
        "spread": "0px"
      }
    }
  }
}

color, dimension 같은 원자 타입$value가 string. shadow, typography, border, transition, gradient 같은 composite 타입$value객체다.


What — $type의 완전한 카탈로그

원자 타입 (string 또는 number $value)

$type$value 형식예시
colorhex / rgb / hsl / oklch"#3b82f6", "oklch(0.6 0.2 240)"
dimension"<number><unit>""16px", "1.5rem"
duration"<number>ms""150ms"
fontFamilystring 또는 string 배열"Inter", ["Inter", "sans-serif"]
fontWeightnumber 또는 string400, "bold"
numbernumber1.5
cubicBezier4-tuple 배열[0.4, 0, 0.2, 1]
strokeStyleenum 또는 dashArray 객체"solid", { "dashArray": ["2px","4px"] }

Composite 타입 ($value가 객체)

$type필드
shadowcolor, offsetX, offsetY, blur, spread, inset?
bordercolor, width, style
transitionduration, delay, timingFunction
gradient[{ color, position }] 배열
typographyfontFamily, fontWeight, fontSize, lineHeight, letterSpacing, textTransform, textDecoration

typography 예시

{
  "typography": {
    "heading-lg": {
      "$type": "typography",
      "$value": {
        "fontFamily": "{font.family.sans}",
        "fontWeight": "{font.weight.bold}",
        "fontSize":   "{dim.32}",
        "lineHeight": "1.2",
        "letterSpacing": "-0.02em"
      }
    }
  }
}

composite의 각 필드 자체도 alias 가능. typography.heading-lg.fontSizedim.32를 가리키고, 그것이 "32px"로 풀린다.


What — $extensions로 벤더 확장

DTCG 표준에 없는 메타데이터는 $extensions 안에 역도메인 네임스페이스로 넣는다.

{
  "color": {
    "primary": {
      "$value": "{color.blue.500}",
      "$type": "color",
      "$extensions": {
        "com.figma": {
          "styleId": "S:abc123",
          "componentVariant": "Primary/Default"
        },
        "studio.tokens": {
          "modify": {
            "type": "darken",
            "value": "0.1"
          }
        },
        "co.example.brand": {
          "wcagContrast": 4.7
        }
      }
    }
  }
}

규약:

  • 키는 reverse domain. 충돌 방지.
  • DTCG는 값을 검증하지 않음. 도구가 자기 영역만 읽음.
  • 알 수 없는 $extensions 키는 무시 (forward compatibility).

What — 완전한 실전 예시

다음은 실제로 동작하는 최소 토큰 세트.

tokens/color.primitive.json:

{
  "color": {
    "$type": "color",
    "blue": {
      "400": { "$value": "#60a5fa" },
      "500": { "$value": "#3b82f6" },
      "600": { "$value": "#2563eb" }
    },
    "gray": {
      "50":  { "$value": "#f9fafb" },
      "900": { "$value": "#111827" }
    }
  }
}

tokens/color.semantic.light.json:

{
  "color": {
    "$type": "color",
    "primary": {
      "$value": "{color.blue.500}",
      "$description": "Primary brand — buttons, links, focus rings",
      "$extensions": {
        "com.figma": { "styleId": "S:primary" }
      }
    },
    "primary-hover": { "$value": "{color.blue.600}" },
    "bg": {
      "surface": { "$value": "{color.gray.50}" }
    },
    "fg": {
      "default": { "$value": "{color.gray.900}" }
    }
  }
}

tokens/spacing.json:

{
  "dim": {
    "$type": "dimension",
    "0":  { "$value": "0px" },
    "1":  { "$value": "4px" },
    "2":  { "$value": "8px" },
    "4":  { "$value": "16px" },
    "8":  { "$value": "32px" }
  }
}

tokens/shadow.json:

{
  "shadow": {
    "$type": "shadow",
    "sm": {
      "$value": {
        "color":   "rgba(0,0,0,0.05)",
        "offsetX": "0px",
        "offsetY": "1px",
        "blur":    "2px",
        "spread":  "0px"
      }
    },
    "md": {
      "$value": {
        "color":   "rgba(0,0,0,0.08)",
        "offsetX": "0px",
        "offsetY": "4px",
        "blur":    "8px",
        "spread":  "0px"
      }
    }
  }
}

What-if — DTCG의 함정

함정 1: $type 누락

// ❌ $type 없음
{
  "color": {
    "primary": { "$value": "#3b82f6" }
  }
}

증상: Style Dictionary가 #3b82f6을 string으로 취급해 iOS UIColor 생성 실패. 원인: transform이 어떤 변환을 적용할지$type으로 결정. 대응: 그룹 레벨에 $type 한 번 적기.

함정 2: alias 경로 오타

{
  "color": {
    "primary": { "$value": "{color.bluuue.500}" }  // 오타
  }
}

증상: Style Dictionary가 Reference doesn't exist 에러. 또는 일부 builder는 그대로 출력 → CSS에 var(--color-bluuue-500) 등장. 대응: 빌드에 usesReferences() 체크 활성화. CI에서 dangling reference 실패.

함정 3: alias cycle

{
  "color": {
    "a": { "$value": "{color.b}" },
    "b": { "$value": "{color.a}" }
  }
}

증상: 무한 루프 또는 stack overflow. 대응: Style Dictionary v4는 cycle detection 내장. 명시적 에러로 빠짐.

함정 4: composite 토큰의 alias 깊이

{
  "typography": {
    "heading": {
      "$type": "typography",
      "$value": {
        "fontFamily": "{font.family}",     // ← 이 토큰이 또 alias라면?
        "fontSize":   "{dim.32}"
      }
    }
  }
}

증상: composite 안의 alias가 깊어지면 resolve 순서에 따라 결과가 달라질 수 있음. 대응: composite 안의 alias는 한 단계까지만. 깊은 체인 금지.

함정 5: $value라는 이름의 그룹

// ❌ 절대 금지
{
  "color": {
    "$value": {                  // ← 이게 토큰? 그룹?
      "primary": { ... }
    }
  }
}

DTCG는 $value예약했다. 그룹 이름으로 쓰면 파서가 토큰으로 오해.

함정 6: spec 버전 혼동

  • draft-1(2022): 일부 도구가 value(no $)도 허용 — 비표준
  • 현재 (2024 Editor’s Draft): $value만. Style Dictionary v4는 strict

레거시 토큰 파일이 value로 되어 있다면 $value로 마이그레이션 필수.


Insight — 왜 $인가, 왜 alias는 {}인가

이 두 가지 작은 선택이 DTCG의 우아함을 결정한다.

$의 선택

JSON에는 예약어가 없다. 어떤 키 이름이든 토큰 이름이 될 수 있다 — color, value, type, description. DTCG가 만약 value, type(no $)을 메타 키로 썼다면, 디자이너가 “value”라는 색 이름을 못 쓰게 됐을 것이다.

$ 접두사는 GraphQL의 __typename, BSON의 $set, JSON Schema의 $ref와 같은 escape 전략이다. 사용자 공간과 메타 공간을 문자 하나로 분리한다.

{}의 선택

alias 문법으로 다른 후보들이 있었다:

  • "$ref": "color.blue.500" — JSON Schema 방식
  • "$alias": "color.blue.500" — 명시적
  • "color.blue.500" — alias인지 값인지 구분 불가

DTCG는 string 안에 {}로 감싸는 길을 택했다. 이유:

  1. 문법적으로 안전: #3b82f6{color.blue.500}string 패턴으로 구분 가능
  2. CSS variables와의 시각적 유사성: var(--color-blue-500)와 같은 치환의 인상
  3. composite 안에서도 동작: "fontSize": "{dim.32}"처럼 객체 필드에서도 자연스러움

이 작은 선택 덕에 DTCG JSON을 사람이 읽을 때, alias가 한눈에 보인다. 도구가 아니라 사람을 위한 포맷이라는 철학이 여기 있다.

표준화의 정치

DTCG는 기술적으로 평범하지만 정치적으로 어려운 사례다. Figma, Adobe, Salesforce, Microsoft가 자기 포맷을 양보해야 했다. 2021~2024년의 협상 과정이 GitHub 토론에 공개되어 있다. “왜 value가 아니라 $value인가?”에만 100개 댓글이 달렸다. 표준은 기술이 아니라 정치다.


요약

  • DTCG는 JSON 한 벌로 모든 디자인 토큰을 표현하는 W3C 커뮤니티 표준.
  • 5가지 규칙: ① $value 있으면 토큰 ② $는 예약어 ③ alias는 {path}$type은 그룹 상속 ⑤ composite는 객체 $value.
  • $extensions로 벤더별 확장. reverse domain 키.
  • 모든 주요 도구(Figma, Style Dictionary v4, Tokens Studio, Penpot)가 채택.