Design Tokens dengan Claude Code: dari Figma ke CSS Variables, Tailwind, dan React
Terapkan design tokens dengan Claude Code, Style Dictionary, CSS variables, Tailwind, dan React.
Design tokens adalah kontrak untuk perubahan UI
Design tokens menyimpan keputusan desain sebagai data bernama: warna, spacing, tipografi, radius, shadow, dan state. Daripada menulis #2563eb, 16px, atau 0.5rem di banyak komponen, kamu menyimpan sumber kebenaran di tokens.json, lalu menghasilkan CSS variables, konfigurasi Tailwind, dan nilai yang dikonsumsi komponen React.
Claude Code cocok untuk alur ini karena pekerjaan token bisa dibaca dan diverifikasi. Claude Code dapat memeriksa CSS lama, menemukan nilai hardcoded, menyarankan nama semantic, mengubah komponen, menjalankan build, dan merangkum diff. Prompt “buat UI lebih konsisten” terlalu luas. Prompt yang lebih aman menyebutkan file token, komponen yang boleh diubah, aturan kontras, dan command yang harus dijalankan.
Untuk konteks lanjutan, baca design system dengan Claude Code dan aksesibilitas dengan Claude Code. Referensi resmi yang dipakai: Design Tokens Format Module 2025.10, Claude Code docs, Figma Variables plugin API, Tailwind theme docs, Style Dictionary, MDN CSS custom properties, dan WCAG contrast guidance.
raw token dan semantic token
raw token menjelaskan bahan mentah, misalnya color.blue.600, space.4, font.size.base. semantic token menjelaskan tujuan, misalnya color.action.primary.bg, color.text.muted, color.surface.default. component token menjelaskan aturan lokal komponen, misalnya button.primary.paddingX.
| Jenis | Contoh | Kapan dipakai |
|---|---|---|
| raw | color.blue.600 | Palette, skala spacing, skala tipografi |
| semantic | color.action.primary.bg | Makna produk, theme, dark mode |
| component | button.primary.paddingX | Jika komponen butuh kontrak khusus |
Mulailah dengan raw dan semantic. Jika Button langsung memakai color.blue.600, nama itu bisa menyesatkan setelah rebranding. Jika Button memakai color.action.primary.bg, nilainya bisa berubah tanpa mengubah makna.
Ubah Figma-like spec menjadi kontrak kode
Figma Variables berguna, tetapi Claude Code sebaiknya menerima handoff yang bisa direview, bukan export nama mentah. Figma-like spec menjelaskan source file, mode, perubahan yang diminta, komponen terdampak, dan review rules. Ini menurunkan risk saat nama visual seperti Blue / 600 masuk ke React padahal produk butuh color.action.primary.bg.
{
"figmaSource": {
"file": "Marketing UI Kit",
"collection": "Brand v2",
"modes": ["light", "dark"]
},
"changeRequest": {
"type": "replace-token",
"from": "color.blue.600",
"to": "color.action.primary.bg",
"components": ["Button", "Link", "Card"]
},
"reviewRules": [
"Do not use raw color tokens in component CSS.",
"Keep focus and hover tokens explicit.",
"List affected use case and pitfall before editing files."
]
}
Minta Claude Code membuat impact table dulu, lalu edit setelah direview. Workflow ini menangkap pitfall umum: satu perubahan token bisa memengaruhi Button, Link, Card, dan focus outline sekaligus.
tokens.json yang bisa dijalankan
Simpan sebagai tokens/tokens.json:
{
"color": {
"blue": {
"50": { "$type": "color", "$value": "#eff6ff" },
"600": { "$type": "color", "$value": "#2563eb" },
"700": { "$type": "color", "$value": "#1d4ed8" }
},
"slate": {
"50": { "$type": "color", "$value": "#f8fafc" },
"100": { "$type": "color", "$value": "#f1f5f9" },
"700": { "$type": "color", "$value": "#334155" },
"900": { "$type": "color", "$value": "#0f172a" }
},
"white": { "$type": "color", "$value": "#ffffff" },
"focus": { "$type": "color", "$value": "#f59e0b" },
"surface": {
"default": { "$type": "color", "$value": "{color.white}" },
"muted": { "$type": "color", "$value": "{color.slate.50}" },
"inverse": { "$type": "color", "$value": "{color.slate.900}" }
},
"text": {
"default": { "$type": "color", "$value": "{color.slate.900}" },
"muted": { "$type": "color", "$value": "{color.slate.700}" },
"inverse": { "$type": "color", "$value": "{color.white}" }
},
"action": {
"primary": {
"bg": { "$type": "color", "$value": "{color.blue.600}" },
"bgHover": { "$type": "color", "$value": "{color.blue.700}" },
"text": { "$type": "color", "$value": "{color.white}" }
}
}
},
"dark": {
"color": {
"surface": {
"default": { "$type": "color", "$value": "{color.slate.900}" },
"muted": { "$type": "color", "$value": "{color.slate.700}" }
},
"text": {
"default": { "$type": "color", "$value": "{color.white}" },
"muted": { "$type": "color", "$value": "{color.slate.100}" }
},
"action": {
"primary": {
"bg": { "$type": "color", "$value": "{color.blue.50}" },
"bgHover": { "$type": "color", "$value": "{color.white}" },
"text": { "$type": "color", "$value": "{color.slate.900}" }
}
}
}
},
"space": {
"2": { "$type": "dimension", "$value": "0.5rem" },
"3": { "$type": "dimension", "$value": "0.75rem" },
"4": { "$type": "dimension", "$value": "1rem" },
"6": { "$type": "dimension", "$value": "1.5rem" }
},
"font": {
"size": {
"sm": { "$type": "dimension", "$value": "0.875rem" },
"base": { "$type": "dimension", "$value": "1rem" },
"lg": { "$type": "dimension", "$value": "1.125rem" }
},
"weight": {
"medium": { "$type": "fontWeight", "$value": "500" },
"bold": { "$type": "fontWeight", "$value": "700" }
}
},
"radius": {
"md": { "$type": "dimension", "$value": "0.5rem" },
"lg": { "$type": "dimension", "$value": "0.75rem" }
},
"shadow": {
"button": { "$type": "shadow", "$value": "0 1px 2px rgb(15 23 42 / 0.16)" }
}
}
Generate CSS variables dengan Style Dictionary
npm install --save-dev style-dictionary
Buat style-dictionary.config.js:
export default {
source: ["tokens/tokens.json"],
hooks: {
formats: {
"css/variables-with-dark": ({ dictionary }) => {
const light = dictionary.allTokens
.filter((token) => !token.path.includes("dark"))
.map((token) => ` --${token.name}: ${token.value};`)
.join("\n");
const dark = dictionary.allTokens
.filter((token) => token.path[0] === "dark")
.map((token) => ` --${token.path.slice(1).join("-")}: ${token.value};`)
.join("\n");
return `:root {\n${light}\n}\n\n[data-theme="dark"] {\n${dark}\n}\n`;
}
}
},
platforms: {
css: {
transformGroup: "css",
buildPath: "src/styles/",
files: [{ destination: "tokens.css", format: "css/variables-with-dark" }]
}
}
};
Tambahkan script:
{
"scripts": {
"tokens:build": "style-dictionary build --config style-dictionary.config.js"
}
}
Output CSS:
:root {
--color-action-primary-bg: #2563eb;
--color-action-primary-bg-hover: #1d4ed8;
--color-action-primary-text: #ffffff;
--space-3: 0.75rem;
--space-4: 1rem;
--font-size-base: 1rem;
--font-weight-bold: 700;
--radius-md: 0.5rem;
--shadow-button: 0 1px 2px rgb(15 23 42 / 0.16);
}
[data-theme="dark"] {
--color-surface-default: #0f172a;
--color-text-default: #ffffff;
--color-action-primary-bg: #eff6ff;
--color-action-primary-text: #0f172a;
}
Tailwind dan React Button
/** @type {import('tailwindcss').Config} */
module.exports = {
content: ["./src/**/*.{ts,tsx,js,jsx,mdx}"],
theme: {
extend: {
colors: {
surface: { DEFAULT: "var(--color-surface-default)", muted: "var(--color-surface-muted)" },
text: { DEFAULT: "var(--color-text-default)", muted: "var(--color-text-muted)", inverse: "var(--color-text-inverse)" },
action: { primary: "var(--color-action-primary-bg)", "primary-hover": "var(--color-action-primary-bg-hover)" }
},
spacing: { 3: "var(--space-3)", 4: "var(--space-4)", 6: "var(--space-6)" },
borderRadius: { md: "var(--radius-md)", lg: "var(--radius-lg)" },
boxShadow: { button: "var(--shadow-button)" }
}
}
};
import "./Button.css";
type ButtonProps = {
children: React.ReactNode;
onClick?: () => void;
disabled?: boolean;
};
export function Button({ children, onClick, disabled = false }: ButtonProps) {
return (
<button className="Button" onClick={onClick} disabled={disabled}>
{children}
</button>
);
}
.Button {
background: var(--color-action-primary-bg);
border: 0;
border-radius: var(--radius-md);
box-shadow: var(--shadow-button);
color: var(--color-action-primary-text);
cursor: pointer;
font-size: var(--font-size-base);
font-weight: var(--font-weight-bold);
padding: var(--space-3) var(--space-4);
}
.Button:hover:not(:disabled) {
background: var(--color-action-primary-bg-hover);
}
.Button:focus-visible {
outline: 3px solid var(--color-focus);
outline-offset: 2px;
}
.Button:disabled {
cursor: not-allowed;
opacity: 0.55;
}
Use case, jebakan, dan review
Use case pertama adalah handoff dari Figma ke code. Figma Variables berguna sebagai input, tetapi tokens.json harus menjadi kontrak yang direview di sisi code. Minta Claude Code membandingkan export Figma dengan token saat ini, lalu melaporkan nilai yang ditambah, dihapus, diganti nama, dan komponen yang terdampak.
Use case kedua adalah dark mode. Jangan menggandakan stylesheet setiap komponen. Override semantic tokens seperti surface, text, dan action di [data-theme="dark"].
Use case ketiga adalah rebranding atau refresh landing page. Build token dan screenshot Storybook membantu mencegah drift, yaitu kondisi ketika warna atau spacing yang seharusnya sama mulai berbeda antar halaman.
Use case keempat adalah PR review. Claude Code bisa menandai raw color yang dipakai langsung, file CSS hasil generate yang diedit manual, focus state yang hilang, dan kontras yang tidak memenuhi WCAG AA 4.5:1 untuk teks normal.
Jebakan yang sering terjadi: membuat terlalu banyak component token sejak awal, memberi nama berdasarkan tampilan seperti blueButton bukan tujuan seperti primaryAction, mengedit tokens.css hasil generate, dan menunda pengecekan kontras sampai akhir.
Review checklist:
-
tokens/tokens.jsonadalah satu-satunya sumber edit; CSS generate tidak diedit manual. - Button, Link, dan Card dicek untuk light, dark, hover, disabled, dan focus.
- React dan Tailwind memakai semantic tokens, bukan raw palette tokens.
- PR menjelaskan Figma-like spec, use case, pitfall, risk, dan workflow.
- Kontras WCAG 2.2 AA divalidasi untuk teks dan state interaktif.
Prompt review Claude Code
Design token review task:
- Read tokens/tokens.json, style-dictionary.config.js, src/styles/tokens.css, tailwind.config.js, and src/components/Button.tsx.
- Check that components use semantic tokens, not raw color tokens.
- Verify light and dark theme values for button, surface, and text tokens.
- Flag any generated CSS file that was edited manually.
- Check WCAG 2.2 AA contrast for normal text and button text.
- Suggest the smallest safe diff. Do not rename tokens unless you list every affected component.
- After changes, run npm run tokens:build and the focused component tests.
Penutup
Alur praktisnya adalah menjaga tokens.json sebagai sumber, membuat CSS variables dengan Style Dictionary, memetakannya ke Tailwind, memakai semantic tokens di komponen, lalu mengecek drift dan kontras di setiap PR. Saya memverifikasi contoh ini dengan generate CSS variables dari token file dan menghubungkannya ke background, text color, spacing, radius, shadow, dan focus outline pada React Button. Untuk prompt dan checklist reusable, lihat produk ClaudeCodeLab. Untuk produksi, tambahkan Storybook, axe, dan review screenshot; untuk adopsi tim, lihat training dan konsultasi ClaudeCodeLab.
PDF gratis: cheatsheet Claude Code
Masukkan email dan unduh satu halaman berisi command, kebiasaan review, dan workflow aman.
Kami menjaga datamu dan tidak mengirim spam.
Tentang penulis
Masa
Engineer yang berfokus pada workflow Claude Code praktis dan adopsi tim.
Artikel terkait
Workflow Obsidian ke CLAUDE.md untuk Claude Code
Ubah catatan kerja Obsidian menjadi operating note CLAUDE.md agar konteks tidak dijelaskan ulang.
Claude Code Revenue CTA Routing: dari artikel ke PDF, Gumroad, dan konsultasi
Workflow Claude Code untuk mengarahkan pembaca ke PDF gratis, Gumroad, atau konsultasi sesuai intent.
Aturan handoff tim Claude Code: bukti review, permission, rollback, dan jalur revenue
Format handoff Claude Code untuk tim: bukti, permission rule, rollback, PDF gratis, Gumroad, dan konsultasi.