Design

How to Build a Design System Your AI Coding Tool Actually Follows

N
Nicolas Maes
·January 30, 2026·13 min read

How to Build a Design System Your AI Coding Tool Actually Follows

Every AI-coded app looks the same. Same rounded corners. Same gray buttons. Same spacing. Your Claude Code generation defaults to shadcn/ui. Your Cursor generation defaults to shadcn/ui. Everyone's defaults are the same because AI has no context.

You can fix this in three files.

Most teams think building an AI-readable design system means creating a Figma library, exporting tokens, and hoping Claude understands the structure. It doesn't work. AI systems need structure that is explicit, stored as plain text, and loaded into every generation. You need a design system built for machines, not humans.

This guide shows you exactly what to build, where to put it, and how to make Claude Code, Cursor, and Copilot follow it without question. We're talking about real code examples you can copy and adapt to your brand.

Why Traditional Design Systems Fail with AI Tools

Your Figma library lives in the browser. Your design tokens live in a Figma file someone opens once per sprint. Your component guidelines live in a Notion doc no one updates. None of this gets loaded when you run Claude Code or Cursor.

When you prompt an AI tool to "build a button," it has exactly two sources of truth: (1) the training data (which defaults to shadcn/ui, Tailwind defaults, and whatever was popular in 2023), and (2) whatever you put in the project directory as plain text files.

The problem is scale. If you tell Claude "use our font size scale: 12, 14, 16, 18, 20, 24, 28, 32, 40, 48, 56, 64, 72," it will follow that instruction for this generation. But the next prompt, it forgets. You need the instruction embedded in a file the tool loads automatically.

Traditional design systems fail with AI because they're built for human browsing: pretty Figma designs, polished documentation sites, PDF guidelines. AI tools can't browse Figma. They can't click links. They read text files in your project root.

An AI-readable design system is different. It's three text files: design tokens, component library, and rules. That's it.

The Three Files Every AI-Readable Design System Needs

Every AI-readable design system needs three files: (1) design tokens in globals.css as CSS custom properties with semantic names, defining colors, spacing, radius, shadows, and typography, (2) a pre-styled component library in src/components/ui/ that uses those tokens, so the AI composes existing components instead of inventing new ones, and (3) a rules file (SKILL.md for Claude Code, .cursor/rules/design.mdc for Cursor, .github/copilot-instructions.md for Copilot) that instructs the AI to use those tokens and components.

When Claude Code or Cursor generates code, it loads these three files into context. It sees your exact color tokens, your exact spacing scale, and your exact component library. It generates code that uses what's already there instead of inventing defaults.

File 1: Design Tokens (globals.css)

Design tokens are the single source of truth for your visual decisions: colors, spacing, radius, shadows, typography. You define them once as CSS custom properties with semantic names, and every tool uses them.

For Tailwind v4 projects, you'll use a @theme block. For non-Tailwind projects, just CSS custom properties.

Here's what a minimal token file looks like:

@theme {
  --color-primary: oklch(0.64 0.25 260);
  --color-primary-dark: oklch(0.45 0.20 260);
  --color-background: oklch(0.98 0.01 260);
  --color-text: oklch(0.21 0.04 260);
  --color-gray-300: oklch(0.93 0.01 260);
  --color-gray-500: oklch(0.68 0.01 260);
  --color-gray-700: oklch(0.43 0.01 260);

  --spacing-xs: 0.25rem;
  --spacing-sm: 0.5rem;
  --spacing-md: 1rem;
  --spacing-lg: 1.5rem;
  --spacing-xl: 2rem;
  --spacing-2xl: 3rem;

  --radius-none: 0;
  --radius-sm: 0.25rem;
  --radius-md: 0.5rem;
  --radius-lg: 1rem;
  --radius-full: 9999px;

  --shadow-sm: 0 1px 2px 0 rgba(0, 0, 0, 0.05);
  --shadow-md: 0 4px 6px -1px rgba(0, 0, 0, 0.1);
  --shadow-lg: 0 10px 15px -3px rgba(0, 0, 0, 0.1);

  --font-sans: system-ui, -apple-system, "Segoe UI", Roboto, sans-serif;
  --font-size-xs: 0.75rem;
  --font-size-sm: 0.875rem;
  --font-size-base: 1rem;
  --font-size-lg: 1.125rem;
  --font-size-xl: 1.25rem;
  --font-size-2xl: 1.5rem;
}

Every color, measurement, and font choice is a token. AI reads these and uses them instead of guessing.

File 2: Component Library (src/components/ui/)

Pre-styled components are the second layer. When Claude Code generates a button, it should import from your component library instead of building one from scratch. When it builds a form, it should use your form components.

This forces consistency. Every form in your app uses the same input styling, the same label pattern, the same error message color.

Create a src/components/ui/ directory with pre-styled components. Here's a Button example:

// src/components/ui/button.tsx
import * as React from "react"
import { cn } from "@/lib/utils"

interface ButtonProps extends React.ButtonHTMLAttributes<HTMLButtonElement> {
  variant?: "default" | "secondary" | "outline" | "ghost"
  size?: "sm" | "md" | "lg"
}

const Button = React.forwardRef<HTMLButtonElement, ButtonProps>(
  ({ className, variant = "default", size = "md", ...props }, ref) => (
    <button
      ref={ref}
      className={cn(
        "font-medium transition-colors focus:outline-none focus:ring-2 focus:ring-offset-2",
        {
          "default": "bg-primary text-white hover:bg-primary-dark",
          "secondary": "bg-gray-200 text-gray-900 hover:bg-gray-300",
          "outline": "border border-gray-300 hover:bg-gray-50",
          "ghost": "text-primary hover:bg-primary hover:bg-opacity-10"
        }[variant],
        {
          "sm": "px-3 py-1.5 text-sm",
          "md": "px-4 py-2 text-base",
          "lg": "px-6 py-3 text-lg"
        }[size],
        className
      )}
      {...props}
    />
  )
)
Button.displayName = "Button"

export { Button }

When Claude Code sees this file, it knows to import from @/components/ui/button and use the variant prop. It won't build a one-off button with hardcoded styles.

File 3: Rules File (SKILL.md / .cursorrules / copilot-instructions.md)

The rules file tells the AI tool exactly what to do. It lists your component import paths, your token names, and what to avoid.

For Claude Code, this is SKILL.md in .claude/skills/design/. For Cursor, it's .cursor/rules/design.mdc. For Copilot, it's .github/copilot-instructions.md.

We'll dive deeper into writing these in a moment. For now, know that this is where you encode your constraints in plain English.

Setting Up Design Tokens for Tailwind v4

Tailwind v4 changed how tokens work. Instead of a JavaScript config file, you define tokens in CSS using @theme. This is actually better for AI because tokens are stored as CSS, which every AI tool understands natively.

Here's the complete setup process.

First, create a globals.css file in your project root or src/ directory:

@import "tailwindcss";

@theme {
  /* Color Tokens */
  --color-primary: oklch(0.64 0.25 260);
  --color-primary-light: oklch(0.78 0.18 260);
  --color-primary-dark: oklch(0.45 0.20 260);

  --color-secondary: oklch(0.60 0.22 40);
  --color-secondary-light: oklch(0.75 0.16 40);
  --color-secondary-dark: oklch(0.40 0.18 40);

  --color-success: oklch(0.70 0.20 130);
  --color-warning: oklch(0.75 0.25 50);
  --color-error: oklch(0.60 0.25 10);

  --color-background: oklch(0.98 0.01 260);
  --color-background-alt: oklch(0.95 0.01 260);
  --color-surface: oklch(1 0 0);

  --color-text-primary: oklch(0.21 0.04 260);
  --color-text-secondary: oklch(0.43 0.01 260);
  --color-text-muted: oklch(0.68 0.01 260);

  --color-border: oklch(0.93 0.01 260);
  --color-border-light: oklch(0.96 0.01 260);

  /* Spacing Tokens */
  --spacing-xs: 0.25rem;
  --spacing-sm: 0.5rem;
  --spacing-md: 1rem;
  --spacing-lg: 1.5rem;
  --spacing-xl: 2rem;
  --spacing-2xl: 3rem;
  --spacing-3xl: 4rem;

  /* Border Radius Tokens */
  --radius-none: 0;
  --radius-sm: 0.25rem;
  --radius-md: 0.5rem;
  --radius-lg: 1rem;
  --radius-xl: 1.5rem;
  --radius-full: 9999px;

  /* Shadow Tokens */
  --shadow-xs: 0 1px 2px 0 rgba(0, 0, 0, 0.05);
  --shadow-sm: 0 1px 3px 0 rgba(0, 0, 0, 0.1), 0 1px 2px 0 rgba(0, 0, 0, 0.06);
  --shadow-md: 0 4px 6px -1px rgba(0, 0, 0, 0.1), 0 2px 4px -1px rgba(0, 0, 0, 0.06);
  --shadow-lg: 0 10px 15px -3px rgba(0, 0, 0, 0.1), 0 4px 6px -2px rgba(0, 0, 0, 0.05);
  --shadow-xl: 0 20px 25px -5px rgba(0, 0, 0, 0.1), 0 10px 10px -5px rgba(0, 0, 0, 0.04);

  /* Typography Tokens */
  --font-family-sans: system-ui, -apple-system, "Segoe UI", Roboto, "Helvetica Neue", sans-serif;
  --font-family-mono: "Monaco", "Courier New", monospace;

  --font-size-xs: 0.75rem;
  --font-size-sm: 0.875rem;
  --font-size-base: 1rem;
  --font-size-lg: 1.125rem;
  --font-size-xl: 1.25rem;
  --font-size-2xl: 1.5rem;
  --font-size-3xl: 1.875rem;

  --font-weight-regular: 400;
  --font-weight-medium: 500;
  --font-weight-semibold: 600;
  --font-weight-bold: 700;

  --line-height-tight: 1.2;
  --line-height-normal: 1.5;
  --line-height-relaxed: 1.75;
}

@layer base {
  body {
    @apply font-sans text-text-primary bg-background;
  }

  h1 {
    @apply text-3xl font-bold leading-tight;
  }

  h2 {
    @apply text-2xl font-semibold leading-snug;
  }

  h3 {
    @apply text-xl font-semibold leading-snug;
  }

  p {
    @apply text-base leading-normal text-text-primary;
  }

  a {
    @apply text-primary hover:text-primary-dark transition-colors;
  }
}

Import this file in your Next.js layout, Vue app, or Svelte app's root. Tailwind will generate utility classes from every token automatically.

Building vs Borrowing a Component Library

You have three options for a component library: write your own, customize shadcN, or generate one. Each has tradeoffs.

Option A: Write Your Own from Radix Primitives

Start from Radix UI primitives (unstyled, accessible components) and style them with your tokens. This gives you total control and the lightest code.

The tradeoff: you'll spend 40-80 hours building a full component library (button, input, select, dialog, dropdown, toast, etc.). Only worth it if you're shipping a product you'll maintain for years.

Option B: Customize ShadCN Defaults

ShadCN is pre-built components on Radix. Run npx shadcn-ui@latest init and customize the generated components to use your tokens. You'll replace hardcoded colors and spacing with your CSS custom properties.

This is the middle ground: 12-20 hours of customization, but a solid starting point. The catch is that ShadCN defaults to a specific aesthetic (modern, rounded, soft shadows). If your brand is sharp and minimal, you're fighting the defaults.

Option C: Generate with a Tool (Matchkit, tweakcn)

Design your system visually in Matchkit, set your 11 design axes (colors, spacing, radius, shadows, typography), and download a complete component library. All components use your tokens. No customization needed.

This is the fastest: 5 minutes to download a library. The tradeoff is less customization than Option A. You're choosing from our presets, not building something entirely custom.

For most teams building with AI, Option C wins. You get a consistent library in minutes, and AI has a crystal-clear component structure to follow.

Writing AI Rules Files That Actually Work

The rules file is where most teams fail. They write 500-line documents describing their entire coding philosophy. AI reads the first 150-200 instructions and ignores the rest.

Keep it short. Specific. Actionable. Focus only on design system constraints: what components to use, what tokens to reference, what patterns are forbidden.

Claude Code: SKILL.md

Create .claude/skills/design/SKILL.md:

# Design System

## Always use these components
- Import buttons from `@/components/ui/button`
- Import forms from `@/components/ui/form`
- Import dialogs from `@/components/ui/dialog`
- Import cards from `@/components/ui/card`

## Button rules
- Use `<Button variant="default">` for primary actions
- Use `<Button variant="outline">` for secondary actions
- Use `<Button variant="ghost">` for tertiary actions
- Never hardcode button styles; always use variants
- Sizes: `size="sm"` for compact UIs, `size="md"` for standard, `size="lg"` for prominent

## Color tokens
Use these token names in className, never hardcoded colors:
- `--color-primary` for brand color
- `--color-secondary` for accent
- `--color-success`, `--color-warning`, `--color-error` for feedback
- `--color-text-primary` for body text
- `--color-text-secondary` for secondary text
- `--color-border` for dividers and borders

## Spacing
Use spacing tokens for all margin and padding:
- `--spacing-xs` (0.25rem) for micro spacing (gap between icon and text)
- `--spacing-sm` (0.5rem) for tight spacing
- `--spacing-md` (1rem) for standard spacing
- `--spacing-lg` (1.5rem) for loose spacing
- `--spacing-xl` (2rem) for very loose spacing

Example: `<div className="p-[--spacing-md] gap-[--spacing-sm]">`

## Border radius
- `--radius-none` for no rounding
- `--radius-sm` for subtle rounding (input fields)
- `--radius-md` for standard rounding (cards, buttons)
- `--radius-lg` for prominent rounding
- `--radius-full` for pills and circles

## Typography
- `--font-size-sm` for labels and captions
- `--font-size-base` for body text
- `--font-size-lg` for subheadings
- `--font-size-xl` for section headings
- Use `--font-weight-semibold` for emphasis, never bold for body text
- Use `--line-height-normal` for readable text, `--line-height-tight` for headings

## Never do this
- Don't write inline styles or hardcoded color values
- Don't use `bg-blue-500` or `text-gray-700`; use your tokens
- Don't invent new component names
- Don't use Tailwind utilities without token references
- Don't write custom CSS when a component exists
- Don't use multiple font families; stick to `--font-family-sans`

## Form patterns
- Always use the Form component from `@/components/ui/form`
- Wrap inputs in `<FormField>` for consistency
- Use semantic HTML: `<label>`, `<input>`, `<textarea>`, `<select>`
- Never style form elements directly; use the Form component

Claude Code loads this file and follows every rule during generation. Keep it to under 200 lines. Beyond that, you hit token limits and Claude ignores constraints.

Cursor: .cursor/rules/design.mdc

Create .cursor/rules/design.mdc:

---
globs: ["**/*.tsx", "**/*.jsx"]
---

# Cursor Design System Rules

## Component imports
Always import components from these paths:
- Button: `import { Button } from "@/components/ui/button"`
- Form: `import { Form, FormField, FormControl, FormLabel } from "@/components/ui/form"`
- Card: `import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card"`
- Dialog: `import { Dialog, DialogContent, DialogHeader, DialogTitle } from "@/components/ui/dialog"`
- Input: `import { Input } from "@/components/ui/input"`

Never build custom components when these exist.

## Button usage
- Primary: `<Button variant="default">Action</Button>`
- Secondary: `<Button variant="secondary">Action</Button>`
- Outline: `<Button variant="outline">Action</Button>`
- Ghost: `<Button variant="ghost">Action</Button>`

Size options: `size="sm" | "md" | "lg"`

## Color tokens
Replace all hardcoded colors with these tokens:
- Primary: `bg-[--color-primary] text-white`
- Secondary: `bg-[--color-secondary] text-white`
- Success: `bg-[--color-success] text-white`
- Warning: `bg-[--color-warning] text-white`
- Error: `bg-[--color-error] text-white`
- Text primary: `text-[--color-text-primary]`
- Text secondary: `text-[--color-text-secondary]`
- Border: `border-[--color-border]`

## Spacing tokens
- xs: `p-[--spacing-xs] gap-[--spacing-xs]` (0.25rem)
- sm: `p-[--spacing-sm] gap-[--spacing-sm]` (0.5rem)
- md: `p-[--spacing-md] gap-[--spacing-md]` (1rem)
- lg: `p-[--spacing-lg] gap-[--spacing-lg]` (1.5rem)
- xl: `p-[--spacing-xl] gap-[--spacing-xl]` (2rem)

Use consistent spacing across all layouts.

## Radius tokens
- sm: `rounded-[--radius-sm]` for inputs
- md: `rounded-[--radius-md]` for cards and buttons
- lg: `rounded-[--radius-lg]` for larger elements
- full: `rounded-[--radius-full]` for pills and avatars

## Typography
- Heading 1: `text-[--font-size-3xl] font-bold`
- Heading 2: `text-[--font-size-2xl] font-semibold`
- Heading 3: `text-[--font-size-xl] font-semibold`
- Body: `text-[--font-size-base] leading-[--line-height-normal]`
- Label: `text-[--font-size-sm] font-medium`

## Forbidden patterns
- No hardcoded `bg-blue-500` or `text-gray-700`
- No inline styles
- No custom shadcn component copies
- No multiple font families
- No single-use CSS files
- No Tailwind arbitrary values without token references

## Form patterns
- Always wrap inputs with `<FormField>`
- Use semantic HTML for accessibility
- Never style inputs directly; use the Input component

The globs frontmatter tells Cursor to apply these rules only to TypeScript/JSX files, keeping design rules separate from backend or API code.

Testing Your Design System Setup

Before you hand this off to Claude Code or Cursor, test the setup manually.

Create a test page. Import your Button component. Use your tokens. Run npm run build or pnpm build. Make sure the page renders correctly.

Then open Claude Code and ask it to "build a login form with email and password inputs, a forgot password link, and a sign-in button." Watch where it imports components from. Does it use @/components/ui/button or does it build a custom button? Does it reference your color tokens or hardcode colors?

If it imports your components and uses your tokens, your design system is AI-readable. If it invents new components and ignores tokens, your rules file isn't specific enough or your SKILL.md file isn't placed correctly.

Claude Code checks for SKILL.md in these locations (in order):

Make sure the file exists and is readable. Also make sure your globals.css file is imported in your app's root layout, so the tokens are actually available.

Frequently Asked Questions

Q: What is an AI-readable design system?

A: A design system formatted as text files that AI coding tools load automatically during code generation. Instead of a Figma library humans browse visually, it's a set of CSS custom properties (tokens), pre-styled component files (.tsx), and a markdown rules file (SKILL.md or .cursorrules) that sits in your project directory. The AI reads these on every generation and follows the constraints.

Q: How do I make Claude Code follow my design system?

A: Create a SKILL.md file in .claude/skills/design/ with specific design rules (exact font sizes, color tokens, spacing values, component import paths). Place your design tokens in globals.css and your components in src/components/ui/. Claude reads the SKILL.md on every generation and uses your tokens and components instead of inventing defaults. Keep the file under 200 lines for best results.

Q: Do I need separate design systems for Claude Code and Cursor?

A: The tokens (globals.css) and components (src/components/ui/) are shared. Only the rules file differs: Claude Code reads SKILL.md, Cursor reads .cursor/rules/design.mdc, Copilot reads .github/copilot-instructions.md. The content is nearly identical, just formatted for each tool. A generator like Matchkit outputs all three from one configuration.

Q: How long does it take to set up an AI-readable design system?

A: With a generator tool: 5-15 minutes (configure visuals, download, place files). From scratch: 4-8 hours (design tokens, write components, author rules file). The manual approach requires design vocabulary to choose good values. The generated approach handles those decisions through a visual configurator.


Ready to stop fighting with AI tools over design choices? Try Matchkit free to generate a complete design system in minutes, then export tokens and components your Claude Code and Cursor already understand.

Once you've built your AI-readable design system, the next step is understanding what design decisions actually matter. Read "The 11 Design Decisions That Define How Your App Looks" to see which of those 11 axes your product actually needs to adjust.

#design-systems#ai-coding#claude-code#cursor#skill-md#tokens
All articles

More articles

Design

MCP Servers for Design: How AI Coding Tools Are Getting a Taste Layer

April 5, 2026·7 min read
Case Study

The Design Gap Between $0/mo and $49/mo SaaS (It's 11 CSS Values)

April 2, 2026·7 min read
Case Study

Agency Workflow: From Client Brief to Branded Prototype with AI Coding

March 29, 2026·7 min read