Ask Claude to build a button component using your design system. Chances are it comes back with bg-blue-600, px-4 py-2, and rounded-md. Your brand uses bg-brand-primary, px-6 py-3, and rounded-lg. The model wasn't careless—it generated the most statistically likely Tailwind button from its training data. It just doesn't know your system.
This is the core failure mode of popular UI libraries when used with AI generation, and it gets worse the more you rely on them.
Why Tailwind CSS Causes Hallucinations
Tailwind's utility-first approach is genuinely powerful for human developers. You compose styles from a predictable set of atomic classes. The problem is that composing styles requires knowing which classes exist, which are customized in your project, and what semantic intent connects them.
For a human developer, this knowledge comes from a combination of documentation lookup, muscle memory, and code review. For an AI model, it comes purely from statistical pattern matching against training data. The model has seen thousands of Tailwind codebases and generates what appears most often—not what's correct for your specific configuration.
Your tailwind.config.js customizations rarely make it into the model's context. Pasting the entire config into every prompt is wasteful, the syntax isn't optimized for LLM consumption, and custom utilities still require manual mapping. The model defaults to Tailwind's standard palette and spacing scale regardless.
The deeper problem is semantic. text-gray-700 describes a color value. It does not describe whether the element is body text, a secondary label, or a decorative caption. px-4 describes a measurement. It does not describe whether this is a compact button or a default-sized one. AI models cannot infer semantic intent from utility names alone—they generate based on probability, not meaning.
How shadcn/ui Compounds the Problem
shadcn/ui's approach differs from Tailwind: it provides copy-paste component source code rather than an npm package. This seems more AI-friendly at first because the model can theoretically see the full component implementation.
In practice, it introduces a distinct category of hallucination. When an AI generates code using shadcn components, it frequently references variants that don't exist in your specific project. The standard Button component accepts variant="default" | "destructive" | "outline" | "ghost" | "link". A model that has seen many shadcn-based codebases will confidently generate variant="primary" or variant="outline-primary" because those strings are plausible given the pattern.
The issue scales with component diversity. Every shadcn component is a slightly different version depending on what customizations a project has applied. The model cannot know which variants you've implemented without seeing your exact source. Even when you paste the component into context, the model must parse TypeScript type definitions and infer available props—a process prone to confident error.
The Root Cause: Design Systems Built for Humans
Both Tailwind and shadcn/ui were designed around a critical assumption: human developers will bridge the gap between tooling and design intent. Humans remember conventions, consult documentation when uncertain, and enforce consistency through code review.
AI models do none of these things reliably. They pattern-match against training data. A model that has seen bg-blue-600 on a thousand buttons will generate bg-blue-600—unless it has access to a structured, queryable source of truth that overrides the statistical default.
How FramingUI Solves This
FramingUI's MCP server gives AI models direct, programmatic access to your design system at generation time.
When Claude Code has the FramingUI MCP server active, it can query the available themes, list licensed components, preview component variants with their actual token mappings, and validate screen definitions before generating code. The model isn't guessing—it's reading a structured catalog.
The MCP configuration for Claude Code looks like this:
{
"mcpServers": {
"framingui": {
"type": "stdio",
"command": "npx",
"args": ["-y", "@framingui/mcp-server@latest"]
}
}
}
This single entry in .mcp.json gives Claude access to 16 design system tools. It can ask which button variants are available and receive a definitive list. It can check what CSS variables a specific component uses. It can validate that a screen definition references real components with real props before writing a single line of component code.
The tokens themselves are CSS custom properties—var(--foreground-accent), var(--background-page), var(--foreground-primary). When a model generates a primary button, it outputs the semantic variable name rather than a hardcoded color. If your theme changes, the variable resolves to a new value automatically. No search-and-replace across the codebase.
What This Looks Like in Practice
The practical difference shows up immediately in generated output quality. With Tailwind and no design system context, a primary button generation is a guess. The model picks plausible utility classes and hopes they match your project's conventions.
With FramingUI MCP active, the model queries available components, receives the component's variant definitions and token mappings, and generates code that references your actual design system. A button that looks wrong due to a bad color guess or incorrect padding doesn't happen—the model used the values from the catalog.
This shifts AI code generation from probabilistic to deterministic. The model still makes decisions—layout choices, component composition, interaction patterns—but the design system values themselves come from a reliable source rather than training data interpolation.
Traditional UI libraries are excellent tools for human-driven development. They weren't designed for a world where AI writes a substantial portion of UI code. FramingUI was.