Install any skill in seconds. Free to start, no credit card required.
Get Started Free →하나의 브랜드 토큰을 3계층(DTCG SSOT ↔ 원시 CSS 변수 ↔ semantic/shadcn 롤)으로 양방향 변환합니다. L1 DTCG(W3C `$value`/`$type`/`$description`)를 진실 원천으로 삼아 L2 CSS custom properties와 L3 Tailwind v4 `@theme` + `.dark` 전환 체계를 파생하고, 역방향(코드→DTCG)과 라운드트립 무결성 검사도 지원합니다. handoff 번들의 3계층 인코딩(02-tokens.json / colors_and_type.css / globals.css)을 자동 변환하는 파이프라인입니다. 다음과 같은 요청 시 반드시 이 스킬을 사용하세요: - "DTCG 토큰을 CSS 변수로 변환" - "디자인 토큰 3계층 매핑" - "shadcn semantic 롤 생성" - "Tailwind v4 @theme 토큰 만들어 줘" - "CSS 변수에서 DTCG SSOT 역추출" - "토큰 라운드트립 무결성
.claude/skills/modu-ai-design-tokens-transformer/SKILL.md| Model | Eval pass | Runs |
|---|---|---|
| gemini-3.6-flash | 100% | 13 |
| Test case | Without → With | Effect | Δ tokens | Δ turns |
|---|---|---|---|---|
| case-04 | ✗→✓ | ▲ Improved | 171% | 0% |
| case-06 | ✗→✓ | ▲ Improved | 250% | 0% |
| case-08 | ✗→✓ | ▲ Improved | 201% | 0% |
| case-09 | ✗→✓ | ▲ Improved | 263% | 0% |
| case-13 | ✗→✓ | ▲ Improved | 677% | 0% |
브랜드 토큰은 소비처마다 다른 형태를 요구합니다. 이 스킬은 동일한 브랜드 진실(색·타이포·spacing·radius·shadow·motion)을 3계층으로 인코딩·변환합니다. L1(DTCG)이 SSOT이고, L2/L3은 파생물이며, 역방향 추출과 라운드트립 검사로 계층 간 정합을 보장합니다.
> 이 SKILL.md는 3계층 스키마·양방향 변환 규칙·라운드트립 검사·사용 예시를 모두 포함합니다. 색·폰트 값은 브랜드 무관 플레이스홀더(#RRGGBB, <BrandFont>)로 표기하며, 실제 브랜드 값은 변환 입력(DTCG 소스)에서 주입됩니다.
DTCG 토큰 변환, 3계층 토큰 매핑, CSS custom properties 생성, shadcn semantic 롤, Tailwind v4 @theme, 토큰 라운드트립, design tokens transformer
| 계층 | 형태 | 소비처 | 대표 파일(handoff.zip) | |---|---|---|---| | L1 — DTCG SSOT | W3C Design Tokens ($value/$type/$description), 계층적 그룹 | 브랜드 진실 원천 | assets/round3/02-tokens.json | | L2 — 원시 CSS 변수 | --color-* · --neutral-50..950 · --tracking-* + self-host @font-face | 단일 파일 HTML 렌더 / 카드 | colors_and_type.css | | L3 — semantic/shadcn | --base-background · --base-primary · --base-ring · --base-sidebar-* · --base-chart-1..5 + Tailwind v4 @theme + .dark 전환 | 프로덕션 코드(shadcn 체계) | assets/round3/globals.css |
$value / $type / $description 보유color.{brand,neutral,semantic,dark,gradient} · typography.{family,weight,size,lineHeight,letterSpacing} · spacing · radius · shadow · motion.{duration,easing} · container · logo$description은 출처·의미를 기록 → 브랜드 진실 원천DTCG 그룹별 최소 토큰 세트
| 그룹 | 최소 토큰 | 비고 | |---|---|---| | color.brand | primary, ink, background (+ primary-hover/active, surface) | 주색·본문색·배경 3종이 필수 하한 | | color.neutral | 50·100·200·300·400·500·600·700·800·900·950 | 11단계 휘도 스케일 | | color.semantic | success, warning, danger, info | 상태색 4종 | | color.dark | background, surface, primary, foreground, border | 다크 오버라이드 최소셋 | | color.gradient | signature (+ soft/dark 파생) | 시그니처 자산 | | typography.family | sans (+ latin/mono/serif) | 본문 폰트 필수 | | typography.weight | regular(400), bold(700) (+ medium/semibold/black) | fontWeight | | typography.size | xs…6xl + display | rem/clamp | | typography.lineHeight | tight, normal (+ snug/relaxed) | number | | typography.letterSpacing | display, heading, body, caption | 한국어 자간 인코딩 | | spacing | 0…32 (4px 베이스 스텝) | dimension | | radius | none, sm, md, lg, full (+ xl/pill) | dimension | | shadow | sm, md, lg (+ xs/xl/signature) | shadow | | motion.duration | fast, normal (+ instant/slow/page) | duration | | motion.easing | default (+ bounce/smooth) | cubicBezier | | container·logo | (선택) 브레이크포인트·자산 경로 | logo는 asset 타입 |
$type 허용값
| $type | 값 형태 | 표준 | |---|---|---| | color | hex · rgb() · oklch() | W3C | | dimension | rem · px · em · clamp() | W3C | | fontFamily | 폰트 스택 문자열 | W3C | | fontWeight | 100–900 정수 | W3C | | number | 단위 없는 수(line-height 등) | W3C | | duration | 150ms · 0.4s | W3C | | cubicBezier | cubic-bezier(…) | W3C | | shadow | box-shadow 문자열/객체 | W3C(composite) | | gradient | linear-gradient(…) 문자열 | W3C(composite) — 번들은 문자열 축약형 사용 | | asset | 파일 경로 | 번들 확장(비표준) — 로고 등 자산 |
예시 JSON (브랜드 무관 — 플레이스홀더)
json{ "$schema": "https://design-tokens.github.io/community-group/format/", "$description": "<브랜드> Design Tokens — <출처 가이드라인> 흡수", "$version": "1.0.0", "$updatedAt": "YYYY-MM-DD", "color": { "brand": { "primary": { "$value": "#RRGGBB", "$type": "color", "$description": "<출처> 명시 — 브랜드 주색. 타이틀/CTA/아이콘" }, "ink": { "$value": "#RRGGBB", "$type": "color", "$description": "본문 텍스트 — #000 대체" } }, "neutral": { "500": { "$value": "#RRGGBB", "$type": "color" } } }, "typography": { "letterSpacing": { "display-tight": { "$value": "-0.075em", "$type": "dimension", "$description": "히어로 강조" }, "body": { "$value": "-0.025em", "$type": "dimension" } } } }
$description은 출처·의미(provenance)를 각인하는 자리다 — 값이 어느 가이드라인/시스템에서 왔는지 토큰에 기록해 L1을 진실 원천으로 만든다.
--color-primary / --color-ink / --color-bg / --color-surface--neutral-50 … --neutral-950 (휘도 스케일)--tracking-display-tight 등 자간 변수(한국어 자간 규칙 인코딩)@font-face (브랜드 폰트) + CDN 보조 폰트CSS 변수 네이밍 규칙
| 카테고리 | 접두 토큰 | 예 | |---|---|---| | 색 역할 | --color-<역할> | --color-primary·--color-ink·--color-bg·--color-surface | | 휘도 스케일 | --neutral-<50..950> | --neutral-500 | | 전경 | --fg-<1..3>·--fg-on-primary | 본문/보조/플레이스홀더 | | 보더 | --border-<1\|2\|strong>·--border-focus-ring | | | 그라디언트 | --gradient-<이름> (+-soft/-dark) | --gradient-signature | | 폰트/굵기 | --font-<sans\|latin\|mono>·--fw-<regular..black> | | | 자간 | --tracking-<역할> | --tracking-body | | 크기/행간 | --text-<xs..6xl\|display>·--lh-<tight..relaxed> | | | 간격/모서리/그림자 | --space-<0..32>·--radius-<none..full>·--shadow-<xs..xl\|signature> | | | 모션/컨테이너 | --duration-<…>·--easing-<…>·--container-<sm..2xl> | |
[data-theme="dark"] 스코프에서 값만 재정의(변수명 동일).bg←background, fw←weight, tracking←letterSpacing, space←spacing, lh←lineHeight)은 표에 등록된 것만 사용 — 임의 축약 금지.@font-face 블록 템플릿 (self-host)
css/* 굵기 1개당 @font-face 1개 반복 */ @font-face { font-family: "<BrandFont>"; font-weight: 400; /* 각 굵기별로 반복 (100~900) */ font-style: normal; font-display: swap; /* FOIT 방지 */ src: url("./fonts/<BrandFont>-Regular.otf") format("opentype"); } /* 보조 라틴/모노 폰트는 CDN @import */ @import url("https://fonts.googleapis.com/css2?family=<Latin>:wght@400;600;700&display=swap");
자간 변수 매핑표 (한국어 타이포 인코딩)
| 자간 변수 | 값 | 적용 | 명명(−N) | |---|---|---|---| | --tracking-display-tight | -0.075em | 히어로 대형 강조 | −75 | | --tracking-display | -0.05em | 메인 타이틀 | −50 | | --tracking-heading | -0.05em | H1–H4 | −50 | | --tracking-body | -0.025em | 본문 | −25 | | --tracking-body-tight | -0.05em | 밀집 본문 | −50 | | --tracking-caption | 0 | 캡션/라벨 | 0 |
패턴(값은 브랜드마다 조정): 텍스트가 클수록 음의 자간을 강하게 준다 — 한국어 대형 타이포는 글자 사이 공백이 과해 보이므로 조이고, 캡션·소형 텍스트는 0으로 둔다. −25/−50/−75 명명은 -0.025/-0.05/-0.075em의 축약이다.
--base-* 의미 롤: background/foreground/card/popover/surface/muted/primary/secondary/accent/destructive/border/input/ring/border-strong/chart-1..5/sidebar-*@theme + @layer base/utilities.dark 클래스 → CSS 변수 전환으로만 다크모드 ([HARD] dark: Tailwind 유틸리티 직접 사용 금지)--base-* → shadcn 컴포넌트 롤 매핑표
| --base-* 롤(쌍) | shadcn 소비 컴포넌트 | 파생 원천(L2) | |---|---|---| | background/foreground | body·페이지 루트 | --color-bg/--color-ink | | card/card-foreground | Card·Dialog | surface/ink | | popover/popover-foreground | Popover·Dropdown·Tooltip | surface/ink | | primary/primary-foreground | Button(default)·Tabs active | brand.primary/white | | secondary/secondary-foreground | Button(secondary)·Badge | neutral-100/ink | | muted/muted-foreground | Skeleton·disabled·보조 텍스트 | neutral-100/neutral-600 | | accent/accent-foreground | hover 배경·Calendar | accent/white | | destructive/destructive-foreground | Button(destructive)·Alert | semantic.danger/white | | border·input·ring | 전역 보더·Input 테두리·포커스 링 | neutral-200·neutral-200·primary | | chart-1..5 | Recharts·데이터 시각화 | primary/warning/success/info/danger | | sidebar-* | Sidebar 전용 롤 세트 | surface/ink/primary/muted 미러 |
@theme 블록 템플릿 (Tailwind v4)
css@import 'tailwindcss'; @theme { --font-sans: "<BrandFont>", system-ui, sans-serif; --font-mono: "<Mono>", ui-monospace, monospace; --color-primary-50: #RRGGBB; /* … 950까지 11단계 */ --color-primary-600: #RRGGBB; /* 라이트 주색 */ --color-primary-500: #RRGGBB; /* 다크 주색 */ --color-success: #RRGGBB; --color-warning: #RRGGBB; --color-danger: #RRGGBB; --color-info: #RRGGBB; --radius-sm: 0.5rem; --radius-lg: 1rem; --radius-full: 9999px; --duration-fast: 150ms; --ease-out: cubic-bezier(0.4, 0, 0.2, 1); } :root { /* --base-* 라이트 롤 바인딩 */ } .dark { /* --base-* 다크 오버라이드 */ }
.dark override 규칙
.dark 클래스 → CSS 변수 재정의로만. dark: Tailwind 유틸리티 직접 사용 금지([HARD]).primary: 명도 상향(어두운 배경 대비 확보) — 라이트 hover 톤에 해당하는 밝은 값으로.border/input: 불투명 그레이 → 반투명 화이트 rgba(255,255,255,0.06~0.16).chart-1..5: 전반 명도 상향.gradient: 밝은 스톱으로 교체.:root 상속.$type별 유효성 확인, 그룹 완결성 검사--color-*/--neutral-*/--tracking-* CSS 변수로 평탄화, @font-face·CDN 폰트 블록 생성color.brand.<x>→--color-<x> (brand 제거) · color.brand.background→--color-bg (별칭)color.neutral.<n>→--neutral-<n> · color.semantic.<x>→--color-<x> · color.gradient.<x>→--gradient-<x>typography.family.<x>→--font-<x> · weight.<x>→--fw-<x> · size.<x>→--text-<x> · lineHeight.<x>→--lh-<x> · letterSpacing.<x>→--tracking-<x>spacing.<n>→--space-<n> · radius.<x>→--radius-<x> · shadow.<x>→--shadow-<x> · motion.duration.<x>→--duration-<x> · motion.easing.<x>→--easing-<x>--base-* semantic 롤에 바인딩, @theme 등록, .dark override 세트 생성primary·ring·sidebar-primary·sidebar-ring·chart-1foreground·card-foreground·popover-foreground · brand.background → background · brand.surface → card·popover·surface·sidebarmuted·secondary·sidebar-accent · neutral.200 → border·input · neutral.600 → muted-foregroundsuccess+chart-3 · warning→warning+accent+chart-2 · info→info+chart-4 · danger→destructive+error+chart-5#000000 금지 → ink 대체), 단일 시그니처 그라디언트, 자간 규칙 등을 각 계층 코멘트로 인코딩$description + 파일 최상위 $description / L2 = CSS 헤더 주석(All tokens FROZEN per <출처>) + 대체값 인라인 주석(/* #000 대체 */, /* 변경 금지 */) / L3 = 헤더 주석 + [HARD] 인라인([HARD] 다크모드는 .dark 클래스로만).#000000)이 산출물에 유입됐는지만 대조.#000 금지→ink 대체 · 임의 그라디언트 신설 금지 · 자간 규칙 준수 · dark: 유틸리티 금지. 다른 브랜드엔 다른 금지 목록이 온다.@theme 선언에서 값·의미 역추출$description은 best-effort 유도(원 출처 불명 시 명시)--color-primary→color.brand.primary · --color-bg→color.brand.background · --neutral-<n>→color.neutral.<n> · --color-success류→color.semantic.<x> · --gradient-<x>→color.gradient.<x>--fw-<x>→typography.weight.<x> · --text-<x>→size.<x> · --tracking-<x>→letterSpacing.<x> · --space-<n>→spacing.<n> · --duration-<x>→motion.duration.<x>--base-<role>(L3)은 L2→L3 바인딩이라 L1 그룹에 직접 대응하지 않음 → 바인딩표(정방향 3단계) 역참조로 원천 토큰 복원.$description 유도 정책: ① 선언 인접 인라인 주석을 승격 → ② 파일 헤더에서 출처(가이드라인/시스템명) 추출해 접두 → ③ 표준 토큰이면 카테고리 표준 설명 → ④ 모두 실패 시 창작 금지, "출처 미상" 명시.정규화 규칙 (비교 전 양쪽 값에 동일 적용)
| 항목 | 정규화 | 예 | |---|---|---| | hex 대소문자 | 소문자 통일 | #FFF→#fff | | 3자리 shorthand | 6자리 확장 | #fff→#ffffff | | rgb()/rgba() | 정수 채널·알파 정규화, 필요 시 hex 변환 | rgb(255,255,255)→#ffffff | | oklch() | 색공간 환산 후 비교(무손실 불가 시 허용 오차 명시) | oklch(…)↔hex | | dimension | 단위 통일(rem 우선), em 자간 형태 보존 | 4px↔0.25rem | | 폰트 스택 | 공백·따옴표 정규화 후 순서 보존 비교 | | | gradient | 각도·스톱·색 정규화(색은 위 hex 규칙) | |
diff 리포트 포맷
## 라운드트립 diff (L1 vs L1')
| 왕복 | 토큰 경로 | 원본 | 왕복 후 | 판정 |
|---|---|---|---|---|
| L1→L2→L3→L1' | color.brand.primary | #RRGGBB | #RRGGBB | PASS |
| L1→L2→L3→L1' | typography.letterSpacing.body | -0.025em | -0.025em | PASS |
| L1→L2→L3→L1' | color.gradient.signature | linear-gradient(…) | (스톱 순서 상이) | FAIL |
정합: N PASS / M 불일치## 토큰 변환 완료 (L{src} → L{dst})
### 변환 요약
- 입력 계층: L{n} ([파일])
- 출력 계층: L{m}
- 변환 토큰: 색 N · 타이포 N · spacing N · radius N · shadow N · motion N
### 생성 파일
- [출력 경로]
### 라운드트립 검사
- 왕복 정합: PASS / N개 불일치
- (불일치 시) 계층·토큰 단위 diff
### FROZEN 규칙 검증
- [위반 없음 / 위반 목록]예시 1 — DTCG → shadcn 정방향 (L1 → L3)
입력: 02-tokens.json (L1 DTCG)
동작:
1) color.brand.primary(#RRGGBB) → --color-primary → --base-primary/--base-ring/--base-chart-1
2) @theme에 --color-primary-50..950 등록, :root에 --base-* 롤 바인딩
3) .dark 오버라이드 세트 생성(primary 명도 상향, border 반투명 화이트)
출력: globals.css (@import tailwindcss + @theme + :root + .dark + @layer)
요약: 색 N · 자간 6 · radius 7 · shadow 6 / FROZEN 위반 0예시 2 — globals.css → DTCG 역추출 (L3 → L1)
입력: globals.css (L3)
동작:
1) @theme·:root·.dark 변수 선언 스캔
2) --base-* 롤은 바인딩표 역참조로 원천 토큰 복원, --color-*/--neutral-*는 직접 역매핑
3) 인접 주석에서 $description 유도, 불명은 "출처 미상"
출력: 02-tokens.json' (재구성 DTCG)
요약: 복원 토큰 N · $description 유도 N / 출처 미상 M예시 3 — 라운드트립 검사 (L1 → L2 → L3 → L1')
입력: 02-tokens.json (L1)
동작: 정규화(hex 소문자·shorthand 확장·rgb→hex) 후 L1 ≟ L1'
출력: diff 리포트
결과: 12/13 PASS, 1 불일치(gradient 스톱 순서) → 계층·토큰 단위 표기.dark 클래스 CSS 변수 전환으로만 — Tailwind dark: 유틸리티 직접 사용 금지#000000 등 FROZEN 금지값을 변환 산출물에 통과 금지$description을 임의 창작 금지 — 유도 불가 시 "출처 미상" 명시| 스킬 | 사용 시점 | |---|---| | moai-designer:design-handoff-reader | 선행: 번들에서 3계층 토큰 소스 추출 | | moai-designer:design-system-prep | 선행: 자산→DESIGN.md 합성(토큰 원천) | | moai-designer:design-system-library | 보조: 75개 브랜드 토큰을 변환 입력으로 | | moai-designer:design-sync-upload | 후속: 변환된 토큰을 Claude Design에 업로드 |
| Case | Status | Duration (ms) | Turns | Tokens | Tool calls | ||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| Without | With | Δ | Without | With | Δ | Without | With | Δ | Without | With | Δ | ||
case-02 | fail→fail | 27,852 | 30,761 | +10% | 1 | 1 | 0% | 6,003 | 12,418 | +107% | 0 | 0 | — |
case-01 | fail→fail | 37,182 | 26,887 | -28% | 1 | 1 | 0% | 7,229 | 11,177 | +55% | 0 | 0 | — |
case-03 | fail→fail | 54,555 | 23,278 | -57% | 1 | 1 | 0% | 3,725 | 10,640 | +186% | 0 | 0 | — |
case-04 | fail→pass | 20,158 | 12,631 | -37% | 1 | 1 | 0% | 2,998 | 8,135 | +171% | 0 | 0 | — |
case-05 | pass→pass | 11,002 | 21,901 | +99% | 1 | 1 | 0% | 1,978 | 10,054 | +408% | 0 | 0 | — |
case-06 | fail→pass | 16,252 | 3,476 | -79% | 1 | 1 | 0% | 2,056 | 7,187 | +250% | 0 | 0 | — |
case-07 | pass→pass | 11,223 | 9,841 | -12% | 1 | 1 | 0% | 1,148 | 7,457 | +550% | 0 | 0 | — |
case-08 | fail→pass | 18,791 | 3,625 | -81% | 1 | 1 | 0% | 2,418 | 7,271 | +201% | 0 | 0 | — |
case-09 | fail→pass | 17,759 | 14,243 | -20% | 1 | 1 | 0% | 2,303 | 8,369 | +263% | 0 | 0 | — |
case-10 | pass→pass | 13,912 | 5,259 | -62% | 1 | 1 | 0% | 1,539 | 7,546 | +390% | 0 | 0 | — |
case-11 | pass→pass | 18,521 | 18,668 | +1% | 1 | 1 | 0% | 2,120 | 8,945 | +322% | 0 | 0 | — |
case-12 | pass→pass | 12,897 | 9,195 | -29% | 1 | 1 | 0% | 1,187 | 7,451 | +528% | 0 | 0 | — |
case-13 | fail→pass | 4,783 | 7,731 | +62% | 1 | 1 | 0% | 912 | 7,090 | +677% | 0 | 0 | — |
case-14 | fail→pass | 22,801 | 3,002 | -87% | 1 | 1 | 0% | 2,834 | 7,133 | +152% | 0 | 0 | — |
case-15 | pass→pass | 17,543 | 19,569 | +12% | 1 | 1 | 0% | 2,219 | 9,307 | +319% | 0 | 0 | — |
case-16 | fail→pass | 15,692 | 3,378 | -78% | 1 | 1 | 0% | 1,829 | 7,215 | +294% | 0 | 0 | — |
case-17 | fail→pass | 12,623 | 2,417 | -81% | 1 | 1 | 0% | 1,322 | 6,968 | +427% | 0 | 0 | — |
case-18 | fail→pass | 15,440 | 8,160 | -47% | 1 | 1 | 0% | 1,978 | 7,146 | +261% | 0 | 0 | — |
case-19 | pass→pass | 18,715 | 3,275 | -83% | 1 | 1 | 0% | 2,384 | 7,149 | +200% | 0 | 0 | — |
case-20 | pass→pass | 8,093 | 2,435 | -70% | 1 | 1 | 0% | 493 | 7,036 | +1327% | 0 | 0 | — |
case-21 | fail→fail | 16,804 | 17,460 | +4% | 1 | 1 | 0% | 2,368 | 9,234 | +290% | 0 | 0 | — |
case-22 | fail→fail | 17,790 | 19,705 | +11% | 1 | 1 | 0% | 2,503 | 9,368 | +274% | 0 | 0 | — |
case-23 | fail→fail | 16,832 | 19,418 | +15% | 1 | 1 | 0% | 2,413 | 9,268 | +284% | 0 | 0 | — |
DecimalAI ran this skill against gemini-3.6-flash twice over the same eval suite — once with the skill loaded and once without — and compared the two runs case by case. 23 cases were attempted. The headline lift of +39 percentage points is the difference between those two pass rates over the 23 comparable cases. 1 case got worse with the skill loaded, and it is included in that figure.
Without the skill loaded, the model failed this case. With it loaded, the same prompt on the same model passed. This is one improved case from the latest verified run; every case, including any that regressed, is in the table above.
Other measured skills in the registry, with their headline benchmark lift.