DesignDropv1.0
Sign inGet started

Design Systems · 11 min read

DESIGN.md Best Practices — 10 Tips for Better AI-Generated UI

A field guide for writing DESIGN.md files that produce consistently on-brand output across Cursor, Claude Code, v0, and the rest. Mistakes to avoid, patterns that scale.

By DesignDrop · Published May 9, 2026

The shape of these tips

Each tip is one thing to do (or not do) plus the underlying reason. They're ordered loosely from "do this on day one" to "fix this once your system has 5+ adopters across teams". Skip ahead if you're already a few months in.

1. Name colors by role, never by value

Wrong:

--blue-500: #635BFF
--blue-700: #2A20A8
--gray-100: #F4F2EC

Right:

--primary: #635BFF
--primary-strong: #2A20A8
--surface-2: #F4F2EC

When you decide six months from now to switch the brand from blue to forest green, every component generated against the role-named tokens picks up the new color. Every component generated against --blue-500 is now lying to you. Pay the small naming-discipline cost on day one.

The role taxonomy that scales: --primary, --primary-strong, --primary-soft, --accent, --ink, --ink-2, --ink-3, --bg, --surface, --surface-2, --line, --success, --warning, --error, --info.

2. Always ship a dark theme — even if your product doesn't have one yet

Adding ## Colors / Dark to DESIGN.md takes about three minutes and immediately doubles the amount of work the agent can do for you. The moment a colleague asks for "this dashboard but in dark mode", the file already has the answer.

If you genuinely don't want dark mode in the product UI, ship the dark theme anyway. It still helps the agent reason about contrast — knowing both ends of the value spectrum makes the agent better at choosing surface colors against text colors even within light mode.

A common mistake is to ship a dark theme that's just inverted light. The good dark theme isn't #0E0E10 text on #FFFFFF background flipped to #FFFFFF on #0E0E10. It's a deliberate set of slightly desaturated, slightly warmer near-black surfaces and slightly off-white text. Look at any well-designed dark theme — Linear, GitHub, Vercel — and you'll see the same pattern.

3. Use a real type scale, not five identical sizes

Wrong:

--sm: 14px
--base: 16px
--md: 18px
--lg: 20px
--xl: 24px

That's a 1.07× scale. Headings won't feel like headings. Body won't feel like body. The agent's output will read flat.

Right:

--sm: 14px
--base: 16px
--lg: 20px
--xl: 32px
--2xl: 48px

That's a 1.25× scale (major third) with a deliberate jump at the top for display headlines. The hierarchy is visible without effort. The agent's headings actually look like headings.

For most product UIs, 1.20× to 1.33× is the right range. Below 1.15× the scale collapses; above 1.45× things start feeling editorial. Pick once and don't fiddle.

4. Set radius once, globally, with intent

Radius is the most underrated lever in feeling on-brand. A product with --radius-md: 0px reads brutalist; a product with --radius-md: 16px reads soft and friendly. The same component code with the same colors and the same type produces dramatically different vibes depending on this single value.

Pick once. Be consistent. The four-stop scale that scales is:

--sm: 4px    /* small chips, badges */
--md: 8px    /* default buttons, inputs */
--lg: 12px   /* cards, dialogs */
--pill: 999px /* fully rounded */

Don't add a --xl: 20px because one component "needs" it. The component doesn't need it; you've decided you want a hero card with bigger corners. That decision lives in the component, not the token system.

5. Cap your shadow scale at 3 levels

Most token systems define eight shadow levels. Most products use two. Define three, name them by elevation, and stop:

--shadow-1: 0 1px 2px rgba(0,0,0,0.06)   /* hairline, default cards */
--shadow-2: 0 4px 12px rgba(0,0,0,0.08)  /* hover, lifted cards */
--shadow-3: 0 12px 32px rgba(0,0,0,0.12) /* dialogs, popovers */

The agent will pick the right one based on the component it's generating. More than three and the agent picks at random and your UI feels chaotic.

If your design language is "no shadow, hairline borders only" — Vercel-style — define one shadow at 0 0 0 0 transparent and lean on --line borders instead. Either choice is consistent; mixing both is not.

6. Don't ship spacing tokens you don't use

Tailwind's default spacing scale runs 0 → 96 with twenty-some stops. Most products use 4 8 16 24 32 64. Ship those:

--space-1: 4px
--space-2: 8px
--space-4: 16px
--space-6: 24px
--space-8: 32px
--space-16: 64px

The agent generates more consistent layouts when the token names match the rhythm you actually want. Six values is enough; the discipline forces you to compose space rather than fine-tune it.

7. Set motion durations + easings, then stop animating

Three duration tokens cover everything:

--duration-fast: 150ms   /* hover, focus, color transitions */
--duration-base: 300ms   /* drawer slides, sheet opens */
--duration-slow: 500ms   /* page transitions */

Two easing tokens cover everything:

--ease-out: cubic-bezier(0.16, 1, 0.3, 1)
--ease-in-out: cubic-bezier(0.4, 0, 0.2, 1)

The agent will reach for these instead of inventing one-off 200ms ease durations on every component. The result feels coherent without anyone consciously noticing why.

If your product genuinely needs more motion variants — game UI, animation-heavy marketing sites — add them. If it doesn't, resist the urge to invent.

8. Don't let two systems fight in one app

A common pattern: a team adopts DesignDrop for new components but keeps a legacy CSS file or theme provider running for old ones. Six months later the app has two design languages partially overlapping. Every PR has a debate about which system to use. New developers can't tell what's canonical.

The fix is to commit to one source of truth and migrate everything to it. The migration doesn't have to be all at once — but it has to be directional, with a deletion target for the legacy system. If you can't put a date on retiring the legacy CSS, you'll have it for years.

DesignDrop's exporters are designed to make this straightforward. Export your DESIGN.md as Tailwind @theme and as CSS Custom Properties. Both reference the same tokens. Use one or the other in any given file; they read the same values.

9. Validate your DESIGN.md in CI

npx designdrop validate DESIGN.md exits non-zero on missing required tokens. Add it to your CI:

- name: Validate DESIGN.md
  run: npx designdrop validate DESIGN.md

This catches the class of mistakes where someone hand-edits the file in a PR, removes --primary because they thought it was unused, and ships a broken DESIGN.md to main. The validator also warns when your dark theme is missing roles your light theme defines, which is the second-most-common drift mode.

10. Keep the file under 200 lines

The whole point is small enough to review in a PR. The moment your DESIGN.md is over 200 lines, two things have happened:

  • You've added tokens that aren't actually different from existing ones (--primary-button-bg when --primary already covers it).
  • You've started encoding component-specific decisions in the token layer (--card-padding, --input-border-color).

Both are smells. Component-specific decisions belong in the component. Token tweaks for redundancy can be removed.

If you genuinely need 200+ tokens — large multi-product portfolios, white-label platforms — you've outgrown the format and probably need a multi-file system. DesignDrop will support multi-file DESIGN.md compositions in a future release; for now, treat 200 lines as the ceiling.

Bonus — three things to never put in a DESIGN.md

  1. Component CSS classes. .dd-button { ... } belongs in your component's stylesheet, not in tokens.
  2. JavaScript or behavior. DESIGN.md is declarative tokens only.
  3. Brand strings. Logo URLs, marketing copy, voice-and-tone — none of these are tokens an agent needs to render UI.

A worked example

Putting all ten tips together produces a DESIGN.md like this — under 100 lines, dark theme included, role-named, opinionated:

# DESIGN.md — your-product

<!-- @designdrop name=your-product version=1.0.0 -->

> Indigo accent on warm cream. Editorial type. Soft 8px radius.

## Colors / Light

\`\`\`
--primary: #4F46E5
--primary-strong: #2A20A8
--accent: #C8F03C
--ink: #0E0E10
--ink-2: #3A3A40
--bg: #FBFAF7
--surface: #FFFFFF
--surface-2: #F4F2EC
--line: #E7E4DC
--success: #16A34A
--warning: #F59E0B
--error: #DC2626
\`\`\`

## Colors / Dark

\`\`\`
--primary: #6A6FFF
--primary-strong: #4F46E5
--accent: #C8F03C
--ink: #F4F2ED
--ink-2: #C4C2BC
--bg: #0E0E10
--surface: #131318
--surface-2: #1B1B22
--line: #25252D
--success: #4ADE80
--warning: #FBBF24
--error: #F87171
\`\`\`

## Typography / Family

\`\`\`
--sans: "Inter", system-ui, sans-serif
--mono: "JetBrains Mono", ui-monospace, monospace
\`\`\`

## Typography / Size

\`\`\`
--sm: 14px
--base: 16px
--lg: 20px
--xl: 32px
--2xl: 48px
\`\`\`

## Radius

\`\`\`
--sm: 4px
--md: 8px
--lg: 12px
--pill: 999px
\`\`\`

## Shadow

\`\`\`
--shadow-1: 0 1px 2px rgba(0,0,0,0.06)
--shadow-2: 0 4px 12px rgba(0,0,0,0.08)
--shadow-3: 0 12px 32px rgba(0,0,0,0.12)
\`\`\`

## Spacing

\`\`\`
--1: 4px
--2: 8px
--4: 16px
--6: 24px
--8: 32px
--16: 64px
\`\`\`

## Motion / Duration

\`\`\`
--fast: 150ms
--base: 300ms
--slow: 500ms
\`\`\`

## Motion / Easing

\`\`\`
--out: cubic-bezier(0.16, 1, 0.3, 1)
--in-out: cubic-bezier(0.4, 0, 0.2, 1)
\`\`\`

That's everything an AI coding agent needs to render a coherent, on-brand product. Anything more is detail you can add when a real component asks for it.