Ask Claude Code or Cursor to build a button, and it will reach for bg-blue-600. Ask it to build a card, and it will use bg-white. Ask it to build an error message, and it will use text-red-500. Every choice is reasonable. None of them are your design system.
This is the specific failure mode of AI-assisted UI development without design system context. AI tools pattern-match against training data. Without explicit constraints, they produce generically correct code—code that compiles and renders, but doesn't belong to your product.
Why shadcn Alone Isn't Enough for AI Workflows
shadcn is excellent at what it does. It gives you component source code that you own and customize directly. The Radix UI foundation handles accessibility. The Tailwind-based styling is familiar and well-documented.
The problem is that shadcn's design language is expressed as raw Tailwind class names. When AI generates code in a shadcn project, it writes bg-blue-500 instead of bg-brand-primary. It writes text-gray-600 instead of text-text-secondary. These classes work, but they don't carry intent. Six generated components later, you have six different interpretations of what "secondary text" means.
What's missing is a layer that tells AI not just which components to use, but which design decisions to apply.
How Design Tokens Fix AI Code Generation
Design tokens name your decisions explicitly. Instead of a color existing only as a hex value or a Tailwind class, it has a semantic name that describes its purpose.
/* Without tokens: AI must guess */
bg-blue-500 /* Is this brand primary? CTA hover? A random blue? */
/* With tokens: intent is explicit */
var(--color-brand-primary) /* This is the primary brand action color */
When AI has access to your token definitions—the names, their values, and the contexts where they apply—it can make correct decisions rather than statistically likely ones.
FramingUI's components use CSS variables throughout. The Button component's primary variant references var(--bg-primary-default) rather than a hardcoded Tailwind class. When AI imports and uses that component, the token binding is already in place.
import { Button } from '@framingui/ui';
// AI generates this — tokens are already wired inside the component
function LoginForm() {
return (
<div className="space-y-[var(--spacing-4)]">
<Button variant="default">Sign In</Button>
<Button variant="outline">Create Account</Button>
<Button variant="destructive">Delete Account</Button>
</div>
);
}
No bg-blue-600. No hover:bg-blue-700. The correct colors are a property of the component, derived from your token system.
The MCP Layer
The token enforcement inside components is one piece. The other is giving AI tools real-time access to your design system definition before they generate code.
FramingUI's MCP server does this. Once configured, Claude Code can query your available components, inspect token values, and understand your design system structure before writing a single line.
Setup:
npx -y @framingui/mcp-server@latest init
This writes the MCP config and wires the runtime contract. The resulting .mcp.json:
{
"mcpServers": {
"framingui": {
"type": "stdio",
"command": "npx",
"args": ["-y", "@framingui/mcp-server@latest"]
}
}
}
After restarting Claude Code, it has tool access to list components, preview themes, and inspect your token definitions. When you ask it to build a pricing card, it can look up what components are available and what tokens govern their appearance—then generate code that belongs to your system.
OKLCH and Why Color Consistency Matters
FramingUI tokens use OKLCH color space. This matters for AI code generation in a subtle but important way.
HSL lightness isn't perceptually uniform. hsl(220, 100%, 50%) looks noticeably darker than hsl(60, 100%, 50%) even though both specify 50% lightness. This means maintaining consistent visual weight across your palette requires manual tuning at every step.
OKLCH lightness is perceptually uniform. oklch(0.5 0.15 220) and oklch(0.5 0.15 60) actually look equally bright. When AI generates a new component that should have "medium emphasis," it can use the 0.5 lightness tier and be confident the result will look consistent with the rest of your palette.
It also makes WCAG contrast compliance more predictable. Text at lightness 0.9 on a background at lightness 0.25 will reliably pass AA contrast requirements regardless of hue.
Migrating from shadcn
If you're already using shadcn components, you don't need to abandon them. You can adopt FramingUI components incrementally—they share compatible import patterns.
// Before (shadcn)
import { Button } from '@/components/ui/button';
// After (FramingUI)
import { Button } from '@framingui/ui';
The API surface is similar by design. The difference is internal: FramingUI components reference your CSS variable tokens rather than raw Tailwind classes. As you add FramingUI components, AI-generated code using them will automatically use your design system instead of guessing.
The combination of token-bound components and AI tool access to your design system definition is what closes the gap. Generated code that is both structurally correct and visually on-brand, without manual correction after each generation.