Originally published on ayurchenko.xyz.

I gave one AI agent the same brief again and again: with no design system, on shadcn/ui, and on a product whose look had been decided before the first screen. Then I measured every element it shipped. The first pass turned out to be the easy part. What a system is for now starts after it.

I have built a design system for every product I have worked on, and over 14 years that is dozens of products. The early ones were Figma libraries and pages of documentation for people. The recent ones live in code, and I implement the components in Storybook myself, because the designer who writes the components is the first to see every gap in the system.

Now agents write a lot of the interface. Founders ask me a fair question. If Claude Code or Cursor builds a screen in minutes, and shadcn/ui gives everyone a component library for free, what is a design system still for? I wanted to answer with numbers instead of an opinion, so I ran two tests.

The setup

The product is Halden, a dashboard for a coffee roaster in Portland with six cafes and a wholesale line. I made it as a test bed for a review tool: an agent built it in one evening on a visual direction I set first, a token file and a small kit, with no design system behind it. The tool and that evening are in A canvas for reviewing AI-built UI.

The agent had six screens to build: the home dashboard, a list of transactions, a customer record, the business settings form, a new wholesale order, and an order with its timeline. Every build got the same written brief, the same data and the same model, Claude Sonnet in Claude Code, in one pass. None of them saw a screenshot of Halden or a line of its CSS.

  • No system, twice. React and Tailwind, no component library, no tokens. What a founder gets from Cursor or Claude Code with a prompt.
  • shadcn/ui, twice. A fresh shadcn setup with its default theme, the stack Lovable starts from.
  • Halden, a decided look with a thin system. Four of the six screens come from Halden itself, built in one evening as a test bed for a review tool on a token file and a small kit: no rules for states, no component descriptions, no checks. Halden had no wholesale section, so the other two are the wholesale screens an agent added in the growth test below, from the build that did have a rules file, a catalog and an audit. I took the versions before any review fixes.

For every screen a script opened the page in headless Chrome at 1440 pixels and read the computed style of every element: how many different buttons and fields there are, which values fall outside the build’s own scale, which looks appear only once. Then it changed the main color, the corner radius and the density in one place, and counted what followed.

What the numbers say

Less than I expected. On the first pass every build came out reasonably consistent.

Across six screens No system (2 runs) shadcn/ui (2 runs) Halden (a decided look, thin system)
Different button looks 9 / 8 13 / 12 12
Different field looks 2 / 3 4 / 3 4
Looks used only once 15 / 17 17 / 15 16
Values off the build’s own scale 7 / 10 6 / 6 21
Raw Tailwind colors around the theme no theme 34 / 24 no Tailwind
Main color change followed 100% / 100% 100% / 100% 100%
Radius change followed 100% / 76% 100% / 100% 79%
Every button has a disabled look no / no yes / yes no
Time to build 6.5 / 9.0 min 6.0 / 5.7 min one evening, with the kit

The builds with no system did not stay without one. Before the first screen, both agents wrote their own small block of tokens and a file of buttons, fields and cards. Tailwind v4 hands them variables for spacing and radius out of the box, so even these builds changed density in one place.

shadcn/ui shipped the most complete states: every button had a disabled look and a loading state. Halden lost on exactly the things a system is for. Five button heights, 21 values off its scale, no disabled look, the browser’s default focus ring. That is what a look without a system produces, and it is the point of this article: decisions give a product its face, and a system is what keeps that face consistent and complete.

So the first pass is no longer where a product goes wrong. An agent on a modern stack gets the basics close enough by itself.

What the numbers miss

Halden home dashboard: light, one sky-blue accent on the chart

First shadcn/ui build of the same dashboard: grey and black

Second shadcn/ui build: nearly identical to the first

Build with no system: a brown palette the agent chose itself

The same dashboard in four builds. Top: Halden, then the first shadcn/ui build. Bottom: the second shadcn/ui build, then the build with no system. Screenshots at 1440 pixels, top of the page.

Put the screens next to each other and the measurements stop being the interesting part. The two shadcn/ui builds are the same product twice: the same grey, the same black bars, the same weight on every card. Any other company that ships the default theme gets this face too, because nobody decided anything about it.

The build with no system picked a brown “coffee” palette on its own. That is a choice, and the agent made it, not the company.

Halden has a look because someone decided it. One blue accent, used only where it carries meaning. A quiet grey for everything else. Density set for a manager who checks numbers between two cafes. None of that comes in a component library. It is a set of decisions, and tokens are the way those decisions get written down so that an agent can follow them.

Where the default theme had no decision, the agents filled the gap themselves. shadcn’s theme has no color for success or warning, so both shadcn builds styled their statuses with raw Tailwind colors: 34 classes in one, 24 in the other, all out of reach of the next theme change.

Halden new wholesale order form

New order form in the shadcn/ui build

New order form in the build with no system

The new order form in three builds: Halden, shadcn/ui, no system. Screenshots at 1440 pixels, top of the page.

A short brief builds a different product every time

I ran a control. The same two stacks, twice each, got a one-paragraph brief, the way founders often write one: six screens and their addresses, the data file, and “make it look good”.

The screens looked fine. The agents chose palettes, added cards nobody asked for, and one rewrote the shadcn theme to match a brand it imagined. But a third of the concrete decisions disappeared: of 79 items in the detailed brief, the builds kept between 43 and 50. No build had a loading or an error state. Two runs of the same stack disagreed about the product itself, bars against a line, today against the last seven days, different cards on the dashboard. Looks used only once rose to between 23 and 29 in three of the four builds.

shadcn/ui build from a one-paragraph brief, first run

The same stack and paragraph, second run, with a different dashboard

Same stack, same paragraph, two runs. Screenshots at 1440 pixels, top of the page.

That is the part an agent does not invent: the decisions. The detailed brief did not make the screens prettier. It made them complete, and the same twice. A design system is that brief, written once, for every screen the product will ever have.

Where drift starts: the second screen, and the fiftieth

The first test I ran was about growth. Three agents added the same six-screen wholesale section to Halden, which already had its token file and component kit in the code. One got only the brief. One also got written guidelines, the kind of page a team keeps in Notion. One got the system in a form an agent reads: a rules file, a catalog that says what each component is for and when not to use it, and an audit it had to run until it reported nothing.

On screen, the three sections looked almost the same. The agents copied the existing screens, mistakes included: two of them took a hard-coded field height from Halden’s settings form straight into their new code. The difference was underneath:

In the new section Brief only Plus written guidelines Plus rules, catalog, audit
Raw values in new code 34 25 0
New CSS classes 31 29 5
Lines of CSS added 233 187 91
Where the styles went a stylesheet for this section a stylesheet for this section shared components, recorded in the catalog
Agent time 8.1 min 7.3 min 4.4 min

The first two forked the product. Each wrote a private stylesheet for one section, with about thirty classes nobody else can reuse and values that will not follow the next token change. Every screen looks right today. The cost arrives with the next fifty screens, when nobody knows which of three similar buttons is the real one.

The third agent added the missing form fields as components of the whole system and wrote them into the catalog, so the next agent finds them. It was also the fastest. A system that answers the agent’s questions saves the time it would otherwise spend guessing.

One more detail. The written guidelines said cards have 16 pixel corners. The code said 24. The agent followed the code. Documentation that lives apart from the code drifts, and an agent always believes the code.

What I put in a system now that agents read it

What changed in the last two years is the reader. A system used to be documentation for designers and engineers. Now it is also the input a coding agent builds from, and the thing its output gets checked against. These are the parts I add for that reader.

  • Code is the source of truth. Tokens and components live in the repository and in Storybook. Figma is where the design work happens and gets synced back. When the two disagree, code wins, and the sync merges changes instead of overwriting them, so values that exist only in code survive it.
  • Names an agent reads the way people do. If a list row lives inside a component called ‚Äúmenu‚Äù, every agent will build menus out of it. Renaming is a design decision.
  • Descriptions written for an agent. Every component says what it is for, when not to use it and what to use instead. The intent lives in one place. A second copy of the same text goes stale, and a stale description misleads an agent more than no description at all.
  • A rules file, tested blind. An agent builds a screen without being told about the system. Then I plant violations in clean code and check that the rules catch every one of them, with no false alarms.
  • States for every component. Hover, focus, disabled, loading, error, empty. Halden skipped them, and the table above shows what that costs.
  • A Figma file an agent can read. An agent checks layer names, component descriptions and token bindings, so that design-to-code reads intent rather than geometry.
  • Checks on what ships. A token audit that flags raw values in new code, and an inventory of the running product that finds what drifted (how I review it). The findings go back to the agent as tasks. Whether a person can finish a task is a separate check: the UX audit agent.
  • A rule for anything new. A new look becomes a variant of an existing component. New behavior gets a component of its own. A new job gets a new component built from the old parts. Anything else turns into a one-off.

This is what the third agent in the growth test read before it touched the wholesale section, shortened. The rules file says where values come from and what to do when nothing fits:

AGENTS.md, excerpt

Read this file and COMPONENTS.md before you write any UI. These rules are checked by npm run audit. Source of truth - styles/tokens.css is the only source of values. Every color, size, space, radius, font size, weight, shadow and duration in new code is a var(--token). No hex, rgb, hsl or oklch literals, and no px values except 0 and 1px borders. - Do not add tokens. If a value you need has no token, use the nearest one and say so in your final message. When nothing fits - Add one component to styles/components.css, built only from tokens, plus an entry in COMPONENTS.md. Then use it everywhere it applies. - Never style an element for one screen only. If a screen needs a look that no component has, that look is a component. Before you finish Run npm run audit. Fix every finding, then run it again. Repeat until it reports 0 findings, or explain in your final message why a finding must stay. 

The catalog answers the question the other two agents answered by guessing: which piece is the right one. Every row says what it is for and what to use instead:

COMPONENTS.md, four of 21 rows

badge(label): every status; tone comes from the tones map. Not for categories or counts. stat({label, value, change, note}): one headline number with an optional change. Not for lists of numbers (use table or .facts). segmented(q, key, options): switching between 2 to 5 views of the same list. Not for filters with many values (use chip). timeline([{label, note, state}]): the steps one record has gone through. Not for steps the user fills in (use .stepper). 

Neither file is long. The rules file is 23 lines and the catalog 29. What makes them work is the last rule: the audit runs until it reports nothing, so the agent cannot finish with a raw value in the code.

Your own system, shadcn’s skeleton, or shadcn as it is

shadcn/ui is a good start, and the test shows why: its components came with the most complete states of any build. The question is what you need on top of it, and that depends on the time you have and what the product has to do.

  • shadcn as it is fits a prototype, an internal tool, or a product where looking like everyone else costs nothing.
  • shadcn‚Äôs skeleton, reworked fits most startups. Keep the behavior and replace the decisions: your color roles, including success and warning, your density, your type, your corners, and component descriptions written for agents.
  • A system of your own fits when the product‚Äôs look is part of what you sell, when several products or brands share one system, or when the team will grow past the point where anyone remembers the exceptions. In my experience shadcn‚Äôs tokens and interactions are not consistent enough from one component to the next for that, so I build from one token logic and one set of interaction rules. It takes longer at the start. Afterwards the team, and its agents, read the logic instead of memorising exceptions.

Whichever you choose, the test that decides it is the same, and it takes more than one round: give an agent a real task, look at what it shipped, find where it guessed, and fix the system there.

Where this comes from

I have built a design system for every product I worked on. The ones I can show, and what they changed, are in the case studies: TrueFi, Deva and 10Planet, Equi and Fully AI.

What to do this week

  • Take an inventory of what your product ships: every button, every field, every value outside your tokens. Count them.
  • Write down the decisions an agent keeps guessing: color roles including success and warning, density, type, corners.
  • Give each component a description: what it is for, when not to use it, what to use instead.
  • Add a rules file, then test it: let an agent build one screen and see where it guessed.
  • Check new code for raw values before every merge.

If you would rather have someone do the first step, that is what the Consistency Review is.

How I ran the test

  • Eleven one-pass builds in total: four from the detailed brief, four from the one-paragraph brief, two of each per stack, plus three in the growth test, one per condition. Claude Sonnet, in the background, checked with a production build only. The agents were not told it was a test.
  • The agents ran with my general coding preferences, which already favor CSS variables. That makes the builds with no system better than a typical one, so the gaps here are conservative.
  • Halden‚Äôs brief did not ask for interaction states. The other builds‚Äô briefs did.
  • Two runs per stack is a small sample. The spread between two runs of one stack was sometimes as large as the spread between stacks.
  • A ‚Äúlook‚Äù is an exact match of computed styles, so two nearly identical buttons count as two.
  • The rebrand check changed the main color, multiplied the corner radius by 1.6 and the density by 1.25, and counted the share of elements that moved.

Cheap and expensive

Anyone can build a product now. An agent will give a founder a working app in an evening, and every screen in it will look reasonable. That is the new floor, and most products will stand on it, in the stack’s default theme or a palette the agent picked, looking like every other app built the same way.

What lifts a product off that floor is a set of decisions the agent will not make: which states every control has, which color means success, how dense a table can get for the person who reads it between two meetings, where the one accent goes and where it never goes, which of three similar buttons is the real one. In both tests, the builds that had those decisions written down came out complete and the same twice. The ones that did not were reasonable and different every time.

Making those decisions well takes taste and having watched real people use real products many times. Writing them down so an agent follows them is now part of the same job. Agents made building cheap; the decisions are the expensive part.

This article first appeared on ayurchenko.xyz. I am Andriy Yurchenko, a product designer and design engineer. More of my writing is at ayurchenko.xyz/writing.