Claude Code can build entire features autonomously. But without design constraints, it generates functional UI that looks like every other AI-generated interface: reasonable spacing, generic blues, Tailwind defaults. The code works. The design doesn't match your product.
The fix isn't micromanaging every component Claude generates. It's giving it access to your design system upfront so every decision it makes aligns with your visual language.
The Generic UI Problem
Ask Claude Code to build a dashboard and you'll get something like this:
<div className="p-6 bg-white rounded-lg shadow">
<h2 className="text-xl font-bold text-gray-900 mb-4">Dashboard</h2>
<div className="grid grid-cols-3 gap-4">
<div className="p-4 bg-blue-50 border border-blue-200 rounded">
<p className="text-sm text-gray-600">Total Users</p>
<p className="text-2xl font-semibold text-gray-900">1,234</p>
</div>
{/* more cards... */}
</div>
</div>
This code:
- ✅ Works correctly
- ✅ Follows common patterns
- ✅ Uses semantic HTML
- ❌ Doesn't match your brand colors
- ❌ Ignores your spacing system
- ❌ Uses arbitrary Tailwind defaults
- ❌ Won't survive design review
The problem compounds. Claude generates dozens of components across multiple features. Each one makes independent styling decisions. Six months later, you have:
- 12 different shades of blue
- Spacing that's "close enough" but inconsistent
- Border radius values ranging from 4px to 12px
- Text sizes that don't match your typography scale
Refactoring this manually is weeks of work.
What Happens When You Add Design Tokens
Give Claude Code access to structured design tokens and the same prompt produces:
<div className="p-6 bg-base rounded-lg shadow-sm border border-default">
<h2 className="text-xl font-semibold text-primary mb-4">Dashboard</h2>
<div className="grid grid-cols-3 gap-4">
<div className="p-4 bg-subtle border border-default rounded-md">
<p className="text-sm text-secondary">Total Users</p>
<p className="text-2xl font-semibold text-primary">1,234</p>
</div>
{/* more cards... */}
</div>
</div>
Now every value comes from your design system:
bg-base,bg-subtle→ your semantic background colorstext-primary,text-secondary→ your text hierarchyborder-default→ your border tokenp-6,p-4,gap-4,mb-4→ your spacing scalerounded-lg,rounded-md→ your corner radius system
When you update your brand colors, all Claude-generated components update automatically. When you adjust spacing, everything stays proportional. The code Claude writes integrates seamlessly with your existing components.
Setting Up Claude Code with FramingUI Tokens
Step 1: Create a Design System Document
Claude Code works best with clear, structured documentation. Create .claude/design-system.md:
# Design System - UI Generation Rules
When generating any UI component, ALWAYS use design tokens from our system.
Never use hardcoded colors, spacing, or arbitrary Tailwind classes.
## Token Import
```tsx
import { tokens } from '@framingui/tokens'
// or for Tailwind: use preset classes directly
Color Tokens
Backgrounds
bg-base- Main surface (#FFFFFF)bg-subtle- Subdued surface (#F9FAFB)bg-muted- Muted surface (#F3F4F6)bg-emphasis- Emphasized surface (#F3F4F6)
Text
text-primary- Primary text (#111827)text-secondary- Secondary text (#6B7280)text-tertiary- Tertiary text (#9CA3AF)text-disabled- Disabled text (#D1D5DB)
Borders
border-default- Default border (#E5E7EB)border-muted- Muted border (#F3F4F6)border-emphasis- Emphasized border (#D1D5DB)
Brand Colors
primary-solid- Primary action colorprimary-fg- Text on primary backgroundserror,success,warning,info- Status colors
Spacing Scale
Use spacing tokens (base 4px):
1= 4px2= 8px3= 12px4= 16px5= 20px6= 24px8= 32px10= 40px12= 48px16= 64px
Typography
text-xs= 12px / 16px line-heighttext-sm= 14px / 20pxtext-base= 16px / 24pxtext-lg= 18px / 28pxtext-xl= 20px / 28pxtext-2xl= 24px / 32pxtext-3xl= 30px / 36px
Border Radius
rounded-sm= 4pxrounded-md= 6pxrounded-lg= 8pxrounded-xl= 12pxrounded-2xl= 16pxrounded-full= 9999px
Component Patterns
Buttons
// Primary button
<button className="bg-primary-solid text-primary-fg px-4 py-2 rounded-md">
Action
</button>
// Secondary button
<button className="bg-muted text-primary px-4 py-2 rounded-md">
Cancel
</button>
Cards
<div className="bg-base border border-default rounded-lg p-6">
{/* content */}
</div>
Form Fields
<input className="w-full px-3 py-2 bg-base border border-default rounded-md text-primary" />
Rules
- NEVER use
bg-blue-500,text-gray-700, etc. - ALWAYS use semantic tokens:
bg-primary-solid,text-secondary - Spacing must be from the scale (no
p-5,gap-7) - Document which tokens each component uses
- If unsure, ask before generating
### Step 2: Configure Claude Code MCP
If using Claude Desktop with MCP, add FramingUI context to your MCP server configuration:
```json
{
"mcpServers": {
"design-system": {
"command": "npx",
"args": ["-y", "@framingui/mcp-server"],
"env": {
"PROJECT_ROOT": "/path/to/your/project",
"TOKENS_PATH": "./.claude/design-system.md"
}
}
}
}
This makes your design tokens available to Claude Code automatically in every session.
Step 3: Initialize Your Tailwind Config
Update tailwind.config.js to use FramingUI preset:
import { framingUIPreset } from '@framingui/tailwind-preset'
export default {
presets: [framingUIPreset],
content: [
'./src/**/*.{js,jsx,ts,tsx}',
'./app/**/*.{js,jsx,ts,tsx}',
],
theme: {
extend: {
// Your custom overrides
}
}
}
Step 4: Test Generation
Ask Claude Code to build a component:
Prompt:
Build a user profile card component with:
- Avatar (circular)
- Name and role
- Edit and message action buttons
- Use our design tokens
Expected Output:
export function UserProfileCard({
avatar,
name,
role,
onEdit,
onMessage
}: UserProfileCardProps) {
return (
<div className="bg-base border border-default rounded-lg p-6">
<div className="flex items-start gap-4 mb-6">
<img
src={avatar}
alt={name}
className="w-16 h-16 rounded-full object-cover"
/>
<div className="flex-1">
<h3 className="text-lg font-semibold text-primary">{name}</h3>
<p className="text-sm text-secondary">{role}</p>
</div>
</div>
<div className="flex gap-3">
<button
onClick={onEdit}
className="flex-1 bg-primary-solid text-primary-fg px-4 py-2 rounded-md font-medium"
>
Edit Profile
</button>
<button
onClick={onMessage}
className="flex-1 bg-muted text-primary px-4 py-2 rounded-md font-medium"
>
Message
</button>
</div>
</div>
)
}
Notice:
- All colors use semantic tokens
- Spacing follows the scale (
gap-4,mb-6,p-6) - Border radius is from the system (
rounded-lg,rounded-md,rounded-full) - Typography uses defined sizes (
text-lg,text-sm) - Component structure matches your patterns
Real-World Example: Building a Feature
Let's see how token-driven generation works for a complete feature.
Prompt:
Build a notification center feature:
- Dropdown triggered from header icon
- List of notifications (unread highlighted)
- Each notification: icon, title, timestamp, mark as read button
- Footer with "View All" link
- Use design tokens, max height with scroll
Claude Code generates:
'use client'
import { useState } from 'react'
import { Bell, Check, ExternalLink } from 'lucide-react'
interface Notification {
id: string
title: string
timestamp: string
isRead: boolean
icon: React.ReactNode
}
export function NotificationCenter({
notifications
}: {
notifications: Notification[]
}) {
const [isOpen, setIsOpen] = useState(false)
const [items, setItems] = useState(notifications)
const unreadCount = items.filter(n => !n.isRead).length
const markAsRead = (id: string) => {
setItems(items.map(n =>
n.id === id ? { ...n, isRead: true } : n
))
}
return (
<div className="relative">
{/* Trigger */}
<button
onClick={() => setIsOpen(!isOpen)}
className="relative p-2 rounded-md hover:bg-muted transition-colors"
>
<Bell className="w-5 h-5 text-secondary" />
{unreadCount > 0 && (
<span className="absolute top-1 right-1 w-2 h-2 bg-error rounded-full" />
)}
</button>
{/* Dropdown */}
{isOpen && (
<>
<div
className="fixed inset-0 z-10"
onClick={() => setIsOpen(false)}
/>
<div className="absolute right-0 top-12 z-20 w-96 bg-base border border-default rounded-lg shadow-lg">
{/* Header */}
<div className="px-4 py-3 border-b border-default">
<h3 className="text-base font-semibold text-primary">
Notifications
</h3>
{unreadCount > 0 && (
<p className="text-sm text-secondary mt-1">
{unreadCount} unread
</p>
)}
</div>
{/* List */}
<div className="max-h-96 overflow-y-auto">
{items.length === 0 ? (
<div className="px-4 py-8 text-center text-secondary text-sm">
No notifications
</div>
) : (
items.map((notification) => (
<div
key={notification.id}
className={`px-4 py-3 border-b border-default last:border-0 ${
!notification.isRead ? 'bg-subtle' : ''
}`}
>
<div className="flex items-start gap-3">
<div className="mt-1 text-secondary">
{notification.icon}
</div>
<div className="flex-1 min-w-0">
<p className="text-sm text-primary font-medium">
{notification.title}
</p>
<p className="text-xs text-tertiary mt-1">
{notification.timestamp}
</p>
</div>
{!notification.isRead && (
<button
onClick={() => markAsRead(notification.id)}
className="p-1 hover:bg-muted rounded"
title="Mark as read"
>
<Check className="w-4 h-4 text-secondary" />
</button>
)}
</div>
</div>
))
)}
</div>
{/* Footer */}
<div className="px-4 py-3 border-t border-default">
<a
href="/notifications"
className="flex items-center justify-center gap-2 text-sm text-primary font-medium hover:text-secondary transition-colors"
>
View All
<ExternalLink className="w-4 h-4" />
</a>
</div>
</div>
</>
)}
</div>
)
}
What Claude got right:
- ✅ All colors from token system (
bg-base,text-primary,border-default) - ✅ Spacing scale respected (
px-4 py-3,gap-3,mt-1) - ✅ Semantic colors for states (
bg-subtlefor unread,bg-errorfor badge) - ✅ Typography sizing (
text-sm,text-xs,text-base) - ✅ Border radius consistency (
rounded-md,rounded-lg,rounded-full) - ✅ Hover states using system tokens (
hover:bg-muted)
This component integrates seamlessly with your existing design system. No refactoring needed.
Advanced: Multi-File Features
For larger features, Claude Code can maintain token consistency across multiple files.
Prompt:
Create a settings page with:
1. Sidebar navigation (Account, Security, Notifications, Billing)
2. Content area with forms for each section
3. Save/Cancel buttons
4. All using design tokens
Claude will generate:
SettingsLayout.tsx- Layout with sidebar + contentSettingsSidebar.tsx- Navigation componentAccountSettings.tsx- Account formSecuritySettings.tsx- Security form- etc.
All files use the same token system. All components have consistent styling. The entire feature looks designed, not generated.
Common Patterns Claude Learns
Once you've generated a few components, Claude Code recognizes your patterns:
Status Badges
<span className="inline-flex items-center px-2 py-1 rounded-full text-xs font-medium bg-success/10 text-success">
Active
</span>
Loading States
{isLoading ? (
<div className="flex items-center justify-center p-8">
<div className="w-6 h-6 border-2 border-primary-solid border-t-transparent rounded-full animate-spin" />
</div>
) : (
content
)}
Empty States
<div className="text-center py-12">
<p className="text-base text-secondary mb-2">No items found</p>
<p className="text-sm text-tertiary">Try adjusting your filters</p>
</div>
Troubleshooting
Claude Uses Hardcoded Values
Fix: Reference the design system doc explicitly:
@design-system.md Build a modal component using our tokens
Inconsistent Spacing
Add to .claude/design-system.md:
## Spacing Rules
- Cards: `p-6` (24px padding)
- Form fields: `px-3 py-2` (12px/8px)
- Buttons: `px-4 py-2` (16px/8px)
- Stack spacing: `space-y-4` or `gap-4` (16px)
- Section spacing: `mb-8` or `mb-12` (32px/48px)
Wrong Semantic Tokens
Improve token documentation:
## When to Use Each Token
### Backgrounds
- `bg-base` - Page background, card backgrounds
- `bg-subtle` - Hover states, secondary surfaces
- `bg-muted` - Disabled states, less important areas
- `bg-emphasis` - Highlighted items, selected states
### Text
- `text-primary` - Headings, important content
- `text-secondary` - Body text, descriptions
- `text-tertiary` - Captions, metadata
- `text-disabled` - Disabled form fields, inactive items
Results
With FramingUI tokens + Claude Code:
✅ Zero design drift - Every generated component uses your system ✅ Faster iteration - Build features without styling delays ✅ Team scalability - Anyone using Claude Code gets consistent output ✅ Easy theming - Update tokens, all AI-generated UI updates ✅ Production ready - Generated code passes design review immediately
Next Steps
- Document component patterns - Add your common layouts to
.claude/design-system.md - Create token snippets - Save frequent Claude prompts for reuse
- Audit generated code - Review a few components, refine token docs based on gaps
- Train your team - Share the design system doc so everyone generates consistent UI
Resources: