Advanced

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.

Streamlining Design System Construction With Claude Code

Building a design system involves a lot of work: defining tokens, implementing components, and writing documentation. With Claude Code, you can build a consistent design system in a systematic way.

Defining Design Tokens

Start by defining design tokens for things like color, font, and spacing.

> Define design tokens based on Tailwind CSS variables.
> Support both light and dark modes.
// 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: '"Inter", 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;

Implementing Base Components

Let’s build a Button component with variant support.

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 Component

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";

For dark mode support, see dark mode implementation, and for animations, see animation implementation. For effective Claude Code usage, see 10 productivity tips that 3x your output.

Summary

With Claude Code, you can efficiently build the foundations of a design system, from defining tokens and implementing components to designing variants. Variant management with CVA and accessibility support are all just a natural-language prompt away.

For details, see the official Claude Code documentation.

#Claude Code #design system #components #Storybook #UI

Level up your Claude Code workflow

50 battle-tested prompt templates you can copy-paste into Claude Code right now.

Free

Free PDF: Claude Code Cheatsheet in 5 Minutes

Key commands, shortcuts, and prompt examples on a single printable page.

Download PDF
M

About the Author

Masa

Engineer obsessed with Claude Code. Runs claudecode-lab.com, a 10-language tech media with 2,000+ pages.