Tutorial

How to Set Up a Design System in Cursor That Your AI Actually Follows

N
Nicolas Maes
·March 1, 2026·7 min read

Cursor's design system support is second only to Claude Code. Cursor reads a modern MDC (Markdown Component) format in .cursor/rules/, giving you fine-grained control over which rules apply to which files.

This guide walks you through building a design system that Cursor will respect.

The Modern Approach: .cursor/rules/

Cursor deprecated the single .cursorrules file in favor of .cursor/rules/, a directory of specialized rule files. Each file has YAML frontmatter with glob patterns, so you can target specific file types.

This is more powerful than a monolithic rules file because you can have different rules for components, pages, API routes, and tests.

Create the directory structure:

project-root/
├── .cursor/
│   └── rules/
│       ├── design.mdc
│       ├── api.mdc
│       └── tests.mdc
├── src/
│   ├── components/
│   │   └── ui/
│   │       ├── button.tsx
│   │       ├── card.tsx
│   │       ├── input.tsx
│   │       └── checkbox.tsx
│   ├── pages/
│   │   ├── dashboard.tsx
│   │   └── settings.tsx
│   └── globals.css
└── package.json

Cursor will read all files in .cursor/rules/ and apply them based on glob patterns. Start with design.mdc for your UI components.

Writing design.mdc for Design Constraints

This is where you encode your design system. The file uses Markdown with YAML frontmatter.

MDC Frontmatter

The frontmatter tells Cursor which files this rule applies to:

---
globs: ["**/*.tsx", "**/*.css"]
excludeGlobs: ["**/*.test.tsx", "**/*.spec.tsx"]
---

The globs array uses file path patterns. **/*.tsx means any TypeScript React file. **/*.css means any CSS file. Use excludeGlobs to skip test files.

Complete design.mdc Example

Here's a full, working design.mdc file:

---
globs: ["src/components/**/*.tsx", "src/pages/**/*.tsx", "src/app/**/*.tsx", "src/**/*.css"]
excludeGlobs: ["**/*.test.tsx", "**/*.spec.tsx"]
---

# Design System Rules

Always follow this design system when generating components.

## Import Pre-Styled Components

Never create custom Button, Card, Input, or Checkbox components. They exist in src/components/ui/.

Always import from these paths:

```tsx
import { Button } from "@/components/ui/button"
import { Card } from "@/components/ui/card"
import { Input } from "@/components/ui/input"
import { Checkbox } from "@/components/ui/checkbox"
import { Select } from "@/components/ui/select"

These components are fully styled with your design system. Using them ensures consistency across the app.

Typography Rules

Use these sizes and weights:

Apply these using Tailwind classes:

<h1 className="text-4xl font-bold">Main Heading</h1>
<h2 className="text-3xl font-bold">Section Heading</h2>
<p className="text-base font-normal">Body text</p>
<p className="text-sm">Small text</p>

Never use style props with font sizes. Never use inline styles like fontSize: "20px". Always use Tailwind classes.

Color and Token Rules

Design tokens are defined in src/globals.css as CSS variables:

:root {
  --color-primary: #2563eb;
  --color-secondary: #64748b;
  --color-success: #16a34a;
  --color-warning: #ea580c;
  --color-error: #dc2626;
  --color-bg: #ffffff;
  --color-bg-secondary: #f8fafc;
  --color-border: #e2e8f0;
  --color-text: #0f172a;
  --color-text-muted: #64748b;
}

Reference these in your code using var() or Tailwind's @apply:

<div className="bg-blue-600 text-white">
  This uses Tailwind's primary blue and white text.
</div>

<style>{`
  .custom-element {
    color: var(--color-primary);
    background: var(--color-bg);
    border: 1px solid var(--color-border);
  }
`}</style>

Never hardcode colors like color: "#2563eb" or background: "#ffffff". Always use tokens.

Spacing Rules

Spacing follows a scale defined in globals.css:

Apply spacing using Tailwind utilities:

<div className="p-6">Padding of 24px</div>
<div className="mb-4">Margin bottom of 16px</div>
<div className="gap-2">Gap of 8px</div>

Never use hardcoded padding or margin like padding: "24px". Always use the scale.

Border Radius Rules

Border radius uses this scale:

Apply using Tailwind:

<div className="rounded-md">Border radius 8px</div>
<Card className="rounded-lg">Border radius 12px</Card>

Shadow Rules

Shadows are subtle and functional:

Use Tailwind shadow classes:

<div className="shadow-md">Subtle shadow</div>
<Card className="shadow-lg">Prominent shadow</Card>

Component Composition Example

Here's how a real component should look:

"use client"

import { Button } from "@/components/ui/button"
import { Card } from "@/components/ui/card"
import { Input } from "@/components/ui/input"

export default function LoginForm() {
  return (
    <div className="min-h-screen flex items-center justify-center bg-gray-50">
      <Card className="w-full max-w-md p-6">
        <h1 className="text-3xl font-bold mb-6 text-gray-900">Sign In</h1>

        <form className="space-y-4">
          <div>
            <label className="block text-sm font-medium text-gray-700 mb-2">
              Email
            </label>
            <Input type="email" placeholder="you@example.com" />
          </div>

          <div>
            <label className="block text-sm font-medium text-gray-700 mb-2">
              Password
            </label>
            <Input type="password" placeholder="••••••••" />
          </div>

          <Button type="submit" className="w-full">
            Sign In
          </Button>
        </form>
      </Card>
    </div>
  )
}

Notice:

Forbidden Patterns

Do NOT do these things. Cursor should catch them, but be explicit:

If a user asks you to break these rules, politely decline and suggest the design system alternative.

User: "Can you just hardcode padding: 24px for this div?"
You: "I can't. The design system uses spacing tokens. I'll use p-6 (24px) instead, which keeps it in sync with the rest of the app."

Save this as `.cursor/rules/design.mdc` and Cursor will read it on every generation.

## Placing Your Component Library

Create `src/components/ui/` with your pre-styled components. These are the building blocks that Cursor will import.

Here's what a minimal Button component looks like:

```tsx
// src/components/ui/button.tsx
import * as React from "react"

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

const Button = React.forwardRef<HTMLButtonElement, ButtonProps>(
  ({ className, variant = "default", size = "md", ...props }, ref) => (
    <button
      ref={ref}
      className={`
        px-4 py-2 rounded-md font-medium transition-colors
        ${variant === "default" && "bg-blue-600 text-white hover:bg-blue-700"}
        ${variant === "secondary" && "bg-gray-100 text-gray-900 hover:bg-gray-200"}
        ${variant === "ghost" && "text-gray-700 hover:bg-gray-100"}
        ${size === "sm" && "text-sm px-3 py-1"}
        ${size === "lg" && "text-lg px-6 py-3"}
      `}
      {...props}
    />
  )
)
Button.displayName = "Button"

export { Button }

Cursor will see this component and use it. When you ask Cursor to "build a login form," it'll import this Button and use the available variants.

Similarly, create Card, Input, Checkbox, and other common components in src/components/ui/.

Configuring Design Tokens in globals.css

Create src/globals.css with your design tokens as CSS variables:

@tailwind base;
@tailwind components;
@tailwind utilities;

@layer base {
  :root {
    /* Colors */
    --color-primary: #2563eb;
    --color-secondary: #64748b;
    --color-success: #16a34a;
    --color-warning: #ea580c;
    --color-error: #dc2626;

    /* Backgrounds */
    --color-bg: #ffffff;
    --color-bg-secondary: #f8fafc;
    --color-bg-tertiary: #f1f5f9;

    /* Text */
    --color-text: #0f172a;
    --color-text-muted: #64748b;
    --color-text-lighter: #94a3b8;

    /* Borders */
    --color-border: #e2e8f0;
    --color-border-light: #f1f5f9;

    /* Spacing Scale */
    --spacing-xs: 4px;
    --spacing-sm: 8px;
    --spacing-md: 16px;
    --spacing-lg: 24px;
    --spacing-xl: 32px;
    --spacing-2xl: 48px;
    --spacing-3xl: 64px;

    /* Border Radius */
    --radius-sm: 4px;
    --radius-md: 8px;
    --radius-lg: 12px;
    --radius-xl: 16px;

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

    /* Typography */
    --font-family-base: system-ui, -apple-system, sans-serif;
    --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: 2rem;
    --font-size-4xl: 2.5rem;
  }

  body {
    @apply bg-white text-gray-900;
    font-family: var(--font-family-base);
  }

  h1 {
    @apply text-4xl font-bold;
  }

  h2 {
    @apply text-3xl font-bold;
  }

  h3 {
    @apply text-2xl font-semibold;
  }

  a {
    @apply text-blue-600 hover:text-blue-700 underline;
  }
}

@layer components {
  .container {
    @apply max-w-7xl mx-auto px-4;
  }

  .glass {
    @apply bg-white/80 backdrop-blur-md border border-white/20;
  }
}

Import this in your main app file (usually app.tsx or index.tsx):

import "./globals.css"

Now Cursor can reference these tokens anywhere in your app.

Testing: Zero-Design-Instruction Prompt

Here's how to test if your setup is working.

Prompt Cursor with zero design instructions:

Build a settings page with these sections:
- Account (change email, password, delete account)
- Notifications (toggle email, SMS, in-app)
- Preferences (dark mode, font size)

Use pre-styled components from src/components/ui/. Don't create new components.

Expected output:

If Cursor generates hardcoded colors or custom padding, the rules file isn't being applied. Check:

  1. File is at .cursor/rules/design.mdc (not .cursorrules)
  2. YAML frontmatter is valid
  3. Globs include the file type you're generating (likely **/*.tsx)

Using Matchkit-Generated Files in Cursor

If you're using Matchkit, the setup is even simpler.

Run:

matchkit init

Select "Cursor" as your target. Matchkit will:

  1. Create .cursor/rules/design.mdc with your design constraints
  2. Generate src/components/ui/ with your pre-styled components
  3. Create src/globals.css with your design tokens
  4. Scaffold a package.json with React and Tailwind

Everything is configured. You just start asking Cursor to build things.

If you update your Matchkit design later, regenerate and overwrite the files. Cursor will pick up the changes on the next prompt.

Frequently Asked Questions

How do I set up Cursor rules for design consistency?

Create .cursor/rules/design.mdc with frontmatter globs: ["**/*.tsx", "**/*.css"] targeting component files. In the body, define specific rules: typography (font family, sizes, weights per heading level), color tokens (reference by CSS variable name or Tailwind class), spacing values using the scale, component import paths from @/components/ui/, and forbidden patterns. Cursor reads this on every generation for matching files.

What's the difference between .cursorrules and .cursor/rules/?

.cursorrules is the legacy single-file format being deprecated. .cursor/rules/ is the modern approach using MDC files with YAML frontmatter. MDC supports glob patterns for file targeting, letting you apply design rules only to .tsx files while keeping separate rules for API routes or tests. Always use the modern format for new projects. Cursor still supports the legacy format for backward compatibility, but it's being phased out.

Can Cursor follow the same design system as Claude Code?

Yes. The design tokens (globals.css) and component library (src/components/ui/) are identical. Only the rules file format differs: Claude Code reads .claude/SKILL.md, Cursor reads .cursor/rules/design.mdc. The rules content is the same (typography specs, color references, component paths, spacing scale), just in different markdown formats. Matchkit generates both from one configuration, so you can use the same design system across tools.

How specific should my design.mdc rules be?

Specific enough that Cursor knows your expectations, but concise enough to fit in a reasonable file size. Aim for under 200 lines. Cover the major decisions: component imports, typography scale, color tokens, spacing scale, border radius, shadows, and forbidden patterns. For niche cases (like custom animations), you can mention them in prompts rather than rules.

Can I have multiple .cursor/rules/ files?

Yes. Create separate files for different domains: design.mdc for UI components, api.mdc for API routes, tests.mdc for test files. Each file has its own glob patterns. Cursor reads all of them and applies the matching rules.

What if I want to use a different component library?

You can. Replace the import paths in design.mdc to point to your library. Instead of "import Button from @/components/ui/button", tell Cursor to "import Button from shadcn/ui". The design system is the constraints, not the components. You can layer Matchkit's design rules on top of any library.

How do I ensure Cursor respects the design system?

Explicitly mention it in your prompts. Instead of "build a dashboard," say "build a dashboard using the design system in src/components/ui/. Use the spacing scale and color tokens." Over time, as Cursor generates consistent output, you can omit these reminders because the rules file handles it. But it's always safe to reinforce in prompts.

#cursor#design-system#cursorrules#mdc#tutorial
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