Opinion

Why Vibe Coding Needs Design Systems: Consistency in the AI Era

AI tools generate features fast, but without design constraints, you get inconsistent UI. Why design systems matter in the AI era.

FramingUI Team6 min read

"Vibe coding" is the future: you describe what you want, and AI agents like Claude Code or Cursor build it. No wrestling with CSS. No debugging responsive layouts. Just high-level intent, translated to working code.

But there's a hidden cost. Without design constraints, vibe coding produces visually inconsistent interfaces that don't scale.

This post explains why design systems are essential for AI-generated code—and how to implement them without slowing down your flow.

What is Vibe Coding?

Vibe coding is prompt-driven development. Instead of writing every line yourself, you give the AI high-level instructions:

"Build a user settings page with profile photo upload, email preferences, and a save button"

The AI generates the components, routing, and state management. You review, tweak, and ship.

The Promise

  • Speed: Features in minutes, not hours
  • Focus: Spend time on logic, not layout
  • Accessibility: AI knows ARIA patterns better than most developers

The Problem

  • Inconsistent styling: Every component gets arbitrary colors, spacing, shadows
  • No cohesion: Login page uses blue-600, dashboard uses indigo-500
  • Scaling nightmare: 50 components, 50 different interpretations of "primary button"

The Visual Debt Problem

Traditional codebases accumulate technical debt. AI-generated codebases accumulate visual debt.

Example: Button Styles

Without constraints, AI generates buttons like this:

Login page:

<button className="px-4 py-2 bg-blue-600 text-white rounded-lg">
  Sign In
</button>

Dashboard:

<button className="px-6 py-3 bg-indigo-500 text-white rounded-md shadow-sm">
  Save Changes
</button>

Settings page:

<button className="px-5 py-2.5 bg-blue-700 text-white rounded">
  Update Profile
</button>

Three buttons, three different:

  • Padding scales (px-4/py-2, px-6/py-3, px-5/py-2.5)
  • Colors (blue-600, indigo-500, blue-700)
  • Border radii (rounded-lg, rounded-md, rounded)

Each choice is reasonable in isolation. Together, they create visual chaos.

Why AI Generates Inconsistent UI

AI models are trained on millions of codebases with different design languages. When you ask for a "primary button," the model samples from this distribution:

  • Material Design patterns (Google)
  • Tailwind examples (common in docs)
  • Bootstrap conventions (legacy web)
  • Custom component libraries (GitHub repos)

Without explicit constraints, the model makes statistically common choices, not your design choices.

The Averaging Problem

AI doesn't know that your brand uses:

  • #2563eb for primary actions (not #3b82f6)
  • 8px border radius (not 4px or 6px)
  • 16px/24px vertical spacing (not 12px or 20px)

It guesses based on training data. And guesses vary.

Design Systems as AI Constraints

A design system is a set of rules that AI follows during generation. Instead of:

"Create a button"

You provide:

"Create a button using var(--color-interactive-primary) for background, var(--spacing-component-sm) for padding, and var(--border-radius-md) for corners"

But typing that every time defeats the purpose of vibe coding.

Solution: Embed Design Tokens

Instead of repeating constraints in every prompt, define them once and make them available to the AI:

1. Create a design token file:

{
  "color": {
    "interactive-primary": "#2563eb",
    "interactive-hover": "#1d4ed8"
  },
  "spacing": {
    "component-sm": "0.75rem",
    "component-md": "1rem"
  },
  "border-radius": {
    "md": "0.5rem"
  }
}

2. Reference it in Claude Code instructions (.claude/instructions.md):

All components must use design tokens from `design-tokens.json`.
Never use hardcoded Tailwind utilities like `bg-blue-600` or `p-4`.
Always reference tokens: `bg-[var(--color-interactive-primary)]`.

Now every component AI generates follows your design system—without you specifying values in each prompt.

Real-World Example: Dashboard Redesign

Before: Vibe Coding Without Constraints

Prompt:

"Create a dashboard with stats cards showing users, revenue, and growth"

Generated code:

<div className="grid grid-cols-3 gap-6">
  <div className="p-6 bg-white rounded-lg shadow">
    <p className="text-sm text-gray-600">Total Users</p>
    <p className="text-3xl font-bold text-gray-900">1,234</p>
  </div>
  <div className="p-5 bg-gray-50 rounded-md border border-gray-200">
    <p className="text-xs text-gray-500">Revenue</p>
    <p className="text-2xl font-semibold text-gray-800">$12,345</p>
  </div>
  <div className="p-4 bg-blue-50 rounded border-2 border-blue-200">
    <p className="text-sm text-blue-600">Growth</p>
    <p className="text-4xl font-bold text-blue-900">+23%</p>
  </div>
</div>

Problems:

  • Inconsistent padding (p-6, p-5, p-4)
  • Inconsistent backgrounds (bg-white, bg-gray-50, bg-blue-50)
  • Inconsistent typography (text-3xl, text-2xl, text-4xl)
  • Inconsistent borders (shadow, border, border-2)

After: Vibe Coding With Design Tokens

Same prompt, with design system configured:

Generated code:

<div className="grid grid-cols-3 gap-[var(--spacing-layout-md)]">
  <div className="p-[var(--spacing-component-md)]
                 bg-[var(--color-surface-elevated)]
                 rounded-[var(--border-radius-md)]
                 border border-[var(--color-border-subtle)]">
    <p className="text-[var(--typography-caption-size)]
                  text-[var(--color-text-secondary)]">
      Total Users
    </p>
    <p className="text-[var(--typography-heading-1-size)]
                  font-[var(--typography-heading-1-weight)]
                  text-[var(--color-text-primary)]">
      1,234
    </p>
  </div>
  {/* Remaining cards follow same pattern */}
</div>

Benefits:

  • All cards use identical spacing, colors, typography
  • Switching to dark mode updates all tokens centrally
  • Rebranding changes one file, not 50 components

How to Implement Design Systems for Vibe Coding

1. Define Core Tokens

Start with the minimum:

  • Colors: text, backgrounds, borders, interactive states
  • Spacing: component padding, layout gaps
  • Typography: heading sizes, body text, captions

2. Convert to CSS Variables

Generate tokens.css:

:root {
  --color-text-primary: #1a1a1a;
  --color-interactive-primary: #2563eb;
  --spacing-component-md: 1rem;
  --typography-heading-1-size: 2.25rem;
}

3. Configure AI Instructions

In .claude/instructions.md:

## Design System Rules

1. Use semantic tokens for all styling
2. Never use hardcoded colors or spacing
3. Reference tokens via CSS variables: `var(--token-name)`

## Token Categories
- Colors: `var(--color-*)`
- Spacing: `var(--spacing-*)`
- Typography: `var(--typography-*)`

4. Verify Consistency

Generate a few components and check:

  • Are all buttons using the same color token?
  • Do all cards use the same padding token?
  • Is typography consistent across components?

If not, refine your instructions to be more explicit.

Advanced: MCP for Persistent Context

If you're using Claude Code with MCP (Model Context Protocol), serve design tokens as a persistent resource:

// mcp-server-design-tokens/index.ts
server.resource({
  uri: 'design://tokens',
  name: 'Design Tokens',
  mimeType: 'application/json',
  async get() {
    return JSON.stringify(tokens);
  }
});

Now every Claude Code session has instant access to your design system without injecting it into prompts.

Common Objections

"Design systems slow down iteration"

True for manual development. Not true for AI.

With AI, the iteration speed is the same whether you use tokens or hardcoded values. But with tokens, you iterate on consistent UI. Without them, you iterate on chaos.

"I'll fix styling later"

Visual debt compounds faster than technical debt.

After generating 20 components with arbitrary styles, refactoring them to use tokens takes hours. Defining tokens upfront takes 30 minutes.

"AI should just know my design preferences"

AI models don't remember across sessions. Every new conversation starts fresh.

Unless you embed preferences as persistent context (via MCP or instructions), the AI guesses every time.

Key Takeaways

  1. Vibe coding is powerful but chaotic without design constraints
  2. Design tokens provide structure AI can follow consistently
  3. Define tokens once, reference them in all AI-generated components
  4. Visual debt scales faster in AI development than traditional coding
  5. MCP makes design systems persistent across all sessions

Conclusion

Vibe coding is not just fast—it's the future of how we build software. But speed without structure creates technical and visual debt that's expensive to fix.

Design systems are the missing layer between AI code generation and production-quality interfaces. They don't slow you down. They ensure the code AI generates today still makes sense six months from now.


Next Steps:

Ready to build with FramingUI?

Build consistent UI with AI-ready design tokens. No more hallucinated colors or spacing.

Try FramingUI
Share

Related Posts