How to Build a Design System: Claude Code 활용 가이드
build a design system: Claude Code 활용. 실용적인 코드 예시와 단계별 가이드를 포함합니다.
デザインシステム구축を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를 참고하세요.
정리
Claude Code를 활용하면 デザイン토큰の定義から컴포넌트구현、バリアント설계まで、デザインシステムの基盤を효율적으로구축할 수 있습니다。CVA를 사용한バリアント관리やアクセシビリティ대응も自然言語で指示する만으로す。
자세한 내용은Claude Code공식 문서를 참고하세요.
무료 PDF: 5분 완성 Claude Code 치트시트
이메일 주소만 등록하시면 A4 한 장짜리 치트시트 PDF를 즉시 보내드립니다.
개인정보는 엄격하게 관리하며 스팸은 보내지 않습니다.
이 글을 작성한 사람
Masa
Claude Code를 적극 활용하는 엔지니어. 10개 언어, 2,000페이지 이상의 테크 미디어 claudecode-lab.com을 운영 중.
관련 글
Claude Code/Codex 안전 Agent Harness 설계: 권한, 검증, 롤백
Claude Code와 Codex를 안전하게 운영하기 위한 Agent Harness를 권한 정책, 실행 계획, 검증, 복구 계층으로 설계합니다.
Claude Code 서브에이전트 활용 패턴 10선
Claude Code의 서브에이전트 기능을 활용하는 10가지 실전 패턴. 병렬 처리, 전문화, 컨텍스트 분리로 개발 속도를 두 배로 만드는 방법.
Claude Code Agent SDK 입문 ― 자율 에이전트를 빠르게 구축하는 방법
Claude Code Agent SDK로 자율형 AI 에이전트를 구축하는 방법을 해설합니다. 설정부터 도구 정의, 멀티스텝 실행까지 실전 코드와 함께 소개합니다.