How to Build a Design System with Claude Code
Learn how to build a design system using Claude Code. Includes practical code examples and step-by-step guidance.
デザインシステム構築をClaude Codeで効率化する
デザインシステムの構築は、トークン設計、コンポーネント実装、ドキュメント整備と多くの作業が伴います。Claude Codeを使えば、一貫性のあるデザインシステムを体系的に構築できます。
デザイントークンの定義
まずは色、フォント、スペーシングなどのデザイントークンを定義します。
> TailwindのCSS変数ベースでデザイントークンを定義して。
> ライトモードとダークモード両対応にして。
// tokens/colors.ts
export const colors = {
primary: {
50: "oklch(0.97 0.01 250)",
100: "oklch(0.93 0.03 250)",
200: "oklch(0.86 0.06 250)",
300: "oklch(0.76 0.10 250)",
400: "oklch(0.66 0.15 250)",
500: "oklch(0.55 0.20 250)",
600: "oklch(0.47 0.20 250)",
700: "oklch(0.39 0.18 250)",
800: "oklch(0.33 0.15 250)",
900: "oklch(0.27 0.12 250)",
},
gray: {
50: "oklch(0.98 0.00 0)",
100: "oklch(0.96 0.00 0)",
200: "oklch(0.91 0.00 0)",
300: "oklch(0.83 0.00 0)",
400: "oklch(0.71 0.00 0)",
500: "oklch(0.55 0.00 0)",
600: "oklch(0.45 0.00 0)",
700: "oklch(0.37 0.00 0)",
800: "oklch(0.27 0.00 0)",
900: "oklch(0.18 0.00 0)",
},
} as const;
// tokens/spacing.ts
export const spacing = {
xs: "0.25rem", // 4px
sm: "0.5rem", // 8px
md: "1rem", // 16px
lg: "1.5rem", // 24px
xl: "2rem", // 32px
"2xl": "3rem", // 48px
"3xl": "4rem", // 64px
} as const;
// tokens/typography.ts
export const typography = {
fontFamily: {
sans: '"Noto Sans JP", system-ui, sans-serif',
mono: '"JetBrains Mono", monospace',
},
fontSize: {
xs: ["0.75rem", { lineHeight: "1rem" }],
sm: ["0.875rem", { lineHeight: "1.25rem" }],
base: ["1rem", { lineHeight: "1.5rem" }],
lg: ["1.125rem", { lineHeight: "1.75rem" }],
xl: ["1.25rem", { lineHeight: "1.75rem" }],
"2xl": ["1.5rem", { lineHeight: "2rem" }],
"3xl": ["1.875rem", { lineHeight: "2.25rem" }],
},
} as const;
基本コンポーネントの実装
Buttonコンポーネントをバリアント対応で作ります。
import { type ButtonHTMLAttributes, forwardRef } from "react";
import { cva, type VariantProps } from "class-variance-authority";
import { cn } from "@/lib/utils";
const buttonVariants = cva(
"inline-flex items-center justify-center rounded-md font-medium transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50",
{
variants: {
variant: {
primary: "bg-primary-600 text-white hover:bg-primary-700",
secondary: "bg-gray-100 text-gray-900 hover:bg-gray-200",
outline: "border border-gray-300 bg-transparent hover:bg-gray-50",
ghost: "hover:bg-gray-100",
danger: "bg-red-600 text-white hover:bg-red-700",
},
size: {
sm: "h-8 px-3 text-sm",
md: "h-10 px-4 text-sm",
lg: "h-12 px-6 text-base",
},
},
defaultVariants: {
variant: "primary",
size: "md",
},
}
);
export interface ButtonProps
extends ButtonHTMLAttributes<HTMLButtonElement>,
VariantProps<typeof buttonVariants> {
isLoading?: boolean;
}
export const Button = forwardRef<HTMLButtonElement, ButtonProps>(
({ className, variant, size, isLoading, children, disabled, ...props }, ref) => {
return (
<button
ref={ref}
className={cn(buttonVariants({ variant, size }), className)}
disabled={disabled || isLoading}
{...props}
>
{isLoading && (
<svg className="animate-spin -ml-1 mr-2 h-4 w-4" viewBox="0 0 24 24">
<circle className="opacity-25" cx="12" cy="12" r="10" stroke="currentColor" strokeWidth="4" fill="none" />
<path className="opacity-75" fill="currentColor" d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4z" />
</svg>
)}
{children}
</button>
);
}
);
Button.displayName = "Button";
Inputコンポーネント
import { type InputHTMLAttributes, forwardRef } from "react";
import { cn } from "@/lib/utils";
export interface InputProps extends InputHTMLAttributes<HTMLInputElement> {
label?: string;
error?: string;
helperText?: string;
}
export const Input = forwardRef<HTMLInputElement, InputProps>(
({ className, label, error, helperText, id, ...props }, ref) => {
const inputId = id || label?.toLowerCase().replace(/\s/g, "-");
return (
<div className="space-y-1">
{label && (
<label htmlFor={inputId} className="block text-sm font-medium text-gray-700">
{label}
</label>
)}
<input
ref={ref}
id={inputId}
className={cn(
"block w-full rounded-md border px-3 py-2 text-sm shadow-sm transition-colors",
"focus:outline-none focus:ring-2 focus:ring-primary-500 focus:border-primary-500",
error ? "border-red-500" : "border-gray-300",
className
)}
aria-invalid={!!error}
aria-describedby={error ? `${inputId}-error` : undefined}
{...props}
/>
{error && (
<p id={`${inputId}-error`} className="text-sm text-red-500">{error}</p>
)}
{helperText && !error && (
<p className="text-sm text-gray-500">{helperText}</p>
)}
</div>
);
}
);
Input.displayName = "Input";
ダークモード対応についてはダークモード実装を、アニメーション追加はアニメーション実装をご覧ください。Claude Codeの効果的な使い方は生産性を3倍にする10のTipsを参照してください。
Summary
Claude Codeを使えば、デザイントークンの定義からコンポーネント実装、バリアント設計まで、デザインシステムの基盤を効率的に構築できます。CVAを使ったバリアント管理やアクセシビリティ対応も自然言語で指示するだけです。
詳しくはClaude Code公式ドキュメントを参照してください。
Related Posts
Mastering Claude Code Hooks: Auto-Format, Auto-Test, and More
Mastering Claude Code Hooks: Auto-Format, Auto-Test, and More. A practical guide with code examples.
Claude Code MCP Server Setup and Practical Use Cases
Claude Code MCP Server Setup and Practical Use Cases. A practical guide with code examples.
The Complete Guide to Writing CLAUDE.md: Best Practices for Project Configuration
The Complete Guide to Writing CLAUDE.md: Best Practices for Project Configuration. A practical guide with code examples.