5 Design System Mistakes That Break AI Code Generation
You have a design system. Documented components, style guides, reusable primitives. Then you ask Claude or Cursor to generate a new feature, and the output uses the wrong blue, inconsistent spacing, and border radii that belong to a different product entirely.
This is not an AI capability problem. AI code generators are good at producing plausible UI. The issue is that "plausible" and "consistent with your system" are different things. If your design system is not structured for machine consumption, AI defaults to patterns learned from public repositories—and none of those match your product.
Here are five structural mistakes that cause this, and how to fix each one.
Mistake 1: Tokens Only Exist in Figma
Your designer maintains colors, spacing, and type in Figma. Developers check the inspector and hardcode values:
<button style={{ padding: '12px 24px', background: '#3b82f6', borderRadius: '8px' }}>
Submit
</button>
When AI generates code, it has no access to Figma. It produces what seems reasonable based on training data:
<button className="px-6 py-3 bg-blue-500 rounded-lg">Submit</button>
Close, but wrong. blue-500 is not your primary color. px-6 is not your spacing scale.
The fix: Export tokens to a TypeScript file the AI can import:
// src/design-system/tokens.ts
export const tokens = {
color: {
primary: { solid: 'var(--foreground-accent)', hover: 'var(--color-primary-700)' },
neutral: { text: 'var(--foreground-primary)', muted: 'var(--foreground-secondary)' },
},
spacing: { sm: '0.5rem', md: '1rem', lg: '1.5rem' },
radius: { md: '0.375rem', lg: '0.5rem' },
}
Now the AI can import and reference real values instead of guessing. Point to this file in your CLAUDE.md or AI config so the tool knows where to look.
Mistake 2: Inconsistent Component Naming
Your codebase has PrimaryButton, ButtonPrimary, and MainButton—all doing roughly the same thing. Humans infer the equivalence. AI cannot.
When you ask for "a primary action button," the AI might generate a generic <button> instead of using your component, because it does not recognize which of the three names is canonical.
The fix: Pick one name and enforce it:
// One component, one name, variants via props
export const Button = ({ variant }: { variant: 'primary' | 'secondary' | 'ghost' }) => {
/* ... */
}
Document this in your README: "Use <Button variant="primary">, not PrimaryButton or MainButton." AI reads this. When it needs a button, it generates <Button variant="primary"> consistently.
Mistake 3: Components Without Machine-Readable Documentation
Storybook looks great in a browser. AI does not browse Storybook. It reads code.
A component without JSDoc or explicit TypeScript types forces the AI to guess the API:
// AI has to guess every prop name
export const TextField = (props) => { /* ... */ }
The AI generates <TextField text="Email" onChange={setEmail} />. Your actual API is label and onValueChange. A user reports a bug. You spend time debugging a prop name.
The fix: Add types and a usage example:
/**
* Text input with label and validation support.
*
* @example
* <TextField label="Email" value={email} onValueChange={setEmail} error={errors.email} />
*/
export const TextField = ({
label,
value,
onValueChange,
error,
}: {
label: string
value: string
onValueChange: (value: string) => void
error?: string
}) => { /* ... */ }
The AI reads the JSDoc example and generates the correct prop names. The type signature catches any deviation.
Mistake 4: Magic Numbers in Existing Components
Your spacing system is well defined. But existing components hardcode values:
<div style={{ marginTop: '24px', paddingLeft: '16px' }}>
AI learns from context. When it sees hardcoded values in your codebase, it produces the same pattern for new components. You end up with 12px, 18px, 20px, and 24px scattered throughout—all approximating your 8px grid, none matching exactly.
The fix: Replace hardcoded values with semantic token names:
export const spacing = {
xs: '0.25rem', // 4px — icon gap, tight labels
sm: '0.5rem', // 8px — compact list items
md: '1rem', // 16px — default component padding
lg: '1.5rem', // 24px — section spacing
xl: '2rem', // 32px — page-level margins
}
With comments explaining intent, AI understands not just the value but when to use it. New UI generation stays on the grid.
Mistake 5: Colors Named by Hue, Not Intent
Defining colors like --blue-500 or gray-700 gives AI no signal about usage context. Is blue-500 for primary actions, links, info states, or chart fills?
Result: AI picks blue-500 whenever it needs something blue, regardless of semantic meaning. Your primary action color appears in decorative elements. Your link color ends up on status badges.
The fix: Name tokens by their semantic role:
export const color = {
primary: {
solid: 'var(--foreground-accent)', // primary actions, CTAs
hover: 'var(--color-primary-700)',
text: 'var(--foreground-inverse)', // primary color on light bg
},
neutral: {
solid: 'var(--foreground-primary)', // default text, borders
muted: 'var(--foreground-secondary)', // secondary text
subtle: 'var(--foreground-muted)', // disabled, placeholder
},
feedback: {
success: 'var(--color-success-500)',
error: 'var(--border-error)',
},
}
When AI needs a primary button color, it uses color.primary.solid. When it needs secondary text, color.neutral.muted. The intent is explicit, and the AI makes the right choice without additional instruction.
The Structural Fix That Ties Everything Together
AI does not explore your entire codebase to find design conventions. It looks for a clear entry point. If tokens are scattered across styles/colors.css, theme/spacing.ts, and constants/typography.js, the AI will not connect them.
Create a single import point:
src/
design-system/
tokens.ts ← everything in one place
components/
Button.tsx ← imports from tokens.ts
TextField.tsx
In your CLAUDE.md or project README:
## Design System
All tokens are in `src/design-system/tokens.ts`.
All components import from that file.
When generating UI, import tokens from this path.
One file. One instruction. The AI can follow it consistently.
What Changes After These Fixes
Before:
// AI guesses from public patterns
<button className="px-6 py-3 bg-blue-500 rounded-lg font-semibold">Submit</button>
After:
// AI references your actual system
import { Button } from '@/design-system/components'
<Button variant="primary">Submit</Button>
The AI stops improvising and starts following your system. Consistency holds even when you are generating UI at high velocity, because the constraints are structural rather than instruction-dependent.
The five mistakes above are easy to miss because they only surface when AI code generation reaches a certain volume. Fix them before that point, and AI becomes a reliable way to extend your design system rather than a source of drift.