Why every AI-generated website looks the same

You have seen this website. Slate-grey background, Inter, a headline with a gradient on two words, three feature cards with rounded corners and a small icon at the top left. It is not that the model has bad taste. It is that nobody gave it any, so it fell back to the defaults — and the defaults are shared by every tool that generates UI.

Updated July 27, 2026

The sameness is inherited, not invented

It is tempting to describe this as a taste problem — the model has seen a billion landing pages and regresses to their mean. That is part of it, but it is not the mechanism, and it leads people to the wrong fix (better prompts). The mechanism is more boring and much more actionable: a coding agent asked to build UI reaches for a component library, and that library ships defaults. Those defaults were designed to be inoffensive, which is exactly what makes them universal.

Ask five different tools — Claude Code, Cursor, v0, Lovable, Bolt — for a dashboard, with no other direction. You will get five layouts and one visual identity. They are not copying each other. They are each independently doing the sensible thing with the same starting material.

The four tells, and where each comes from

1. The neutral ramp

shadcn/ui's default theme is built on Tailwind's zinc scale, with slate and gray as the common swaps. It is a cool, slightly blue-tinted grey. Once you have noticed it you cannot stop noticing it: it is the background of most AI-built products shipped in the last two years. It is a genuinely good neutral, which is the problem — it is good enough that nobody replaces it.

The giveaway is not the grey itself but its *uniformity*. A designed interface usually biases its neutrals slightly toward the brand hue, so the greys feel related to the accent. A default interface has a pure ramp sitting next to an unrelated accent, and the two never quite belong to each other.

2. Inter, or something indistinguishable from it

Inter is an excellent UI typeface, which is why it is everywhere and why it now reads as a default rather than a decision. Where it is not literally Inter, it is the font-sans system stack, which lands on a near-identical grotesque on most machines. Either way the page has one family doing every job: headings, body, labels, numbers.

One family is the actual tell, more than which family it is. A designed system almost always pairs — a display face with character for headings against a workhorse for body, or at minimum a deliberate weight and width contrast within one superfamily. Single-family pages read flat even when every other decision is sound.

3. `0.5rem` on everything

shadcn's default --radius is 0.5rem, and it propagates to buttons, inputs, cards, dialogs and popovers alike. Uniform radius across every element size is not how designed systems behave: a 44px button and a 400px card carrying the identical corner radius makes the card look soft and the button look blobby, because radius reads relative to the element it is on.

:root {
  /* The line most AI-built projects never touch */
  --radius: 0.5rem;
}
The single declaration behind a surprising amount of the sameness.

4. The three-card grid

Hero, then three equal cards, then a testimonial band, then a CTA. This one is genuinely training-data gravity rather than a config default — it is the single most common marketing-page structure on the web, so it is the shape a model produces when the brief does not imply another one. It is not wrong. It is simply the layout that carries no information about your product.

Preview unavailable here. Browse complete kits in the kit gallery.

Why better prompting stops working

The common response is to write a longer prompt: *use a warm palette, pair a serif display with a grotesque body, vary the radius.* That works. It works for one screen, and it usually works for the second and third.

Then it degrades, and it degrades in a specific way. The agent is not ignoring your instruction — it is holding an increasingly long conversation in which your styling paragraph is competing with everything since. By screen five it remembers *warm* and has lost the hex values, so it approximates. The approximation is a slightly different warm. Screen six approximates the approximation.

Drift is not the agent forgetting your instruction. It is the agent remembering it approximately, six times in a row.

People in the middle of this call it *design drift*, and it is the reason a vibe-coded app that looked sharp on Friday looks assembled by committee on Monday. The instruction lived in the conversation, and conversations decay.

The test that separates the two failure modes: open screen one and screen eight side by side. If they disagree about spacing and colour but each is internally consistent, that is drift. If both are internally inconsistent, the system was never specified at all.

The fix is a file the agent re-reads, not a paragraph it remembers

A design system stops drift for the same reason it stops it on human teams: the values live somewhere durable and get consulted, rather than being carried in someone's head. For a coding agent, that somewhere is a DESIGN.md plus a token set in the repo. Both are re-read at the start of every session, so screen twenty resolves the same --primary as screen one — not a similar one.

In the promptIn tokens + DESIGN.md
Survives a new sessionNo — context resetsYes — re-read from the repo
Precision after 10 screensApproximate ('warm-ish')Exact hex, every time
Reviewable in a PRNoYes — it is a file that diffs
Works across toolsRewrite per toolSame file for Claude Code, Cursor, v0
The same instruction, held two different ways.

This is the whole argument for giving an agent a real system rather than a better description of one. The semantic token layer matters more than the palette: --primary and --muted-foreground survive a redesign, whereas blue-600 scattered through markup does not.

Token specimen · real values

Terrain Vivant

Live render

Terrain Vivant's actual tokens — the same values its exports use.

Color tokensSemantic roles with HEX / HSL / CMYK

Color tokens

Terrain Vivant

light · HEX · HSL · CMYK

Core

#229F39

background

H 131 · C79, 0, 64, 38

#001500

foreground

H 120 · C100, 0, 100, 92

#2B3386

card

H 235 · C68, 62, 0, 47

#17882A

muted

H 130 · C83, 0, 69, 47

#0C7023

border

H 134 · C89, 0, 69, 56

Brand

#2B3386

primary

H 235 · C68, 62, 0, 47

#FFFFFF

primary-fg

H 0 · C0, 0, 0, 0

#1A4A8C

secondary

H 215 · C81, 47, 0, 45

#095DAC

accent

H 209 · C95, 46, 0, 33

#2B3386

ring

H 235 · C68, 62, 0, 47

Semantic

#B91C1C

destructive

H 0 · C0, 85, 85, 27

#FFFFFF

destructive-fg

H 0 · C0, 0, 0, 0

#004D10

success

H 132 · C100, 0, 79, 70

#92400E

warning

H 23 · C0, 56, 90, 43

#000000

muted-fg

H 0 · C0, 0, 0, 100

Charts

#2B3386

chart-1

H 235 · C68, 62, 0, 47

#095DAC

chart-2

H 209 · C95, 46, 0, 33

#0C549C

chart-3

H 210 · C92, 46, 0, 39

#001500

chart-4

H 120 · C100, 0, 100, 92

#3C438A

chart-5

H 235 · C57, 51, 0, 46

Type scaleHeading, body, and mono in the kit's fonts

Typography

Terrain Vivant

Scale: perfect-fourth

Density: balanced

Heading · Space Mono · 3rem

Sample headline

Subheading · Space Mono · 2.25rem

A bold two-color institutional editorial system built on vivid green and cobalt blue full-screen surfaces, with monospace type throughout and zero-radius geometry.

Body · Space Mono · 1rem

A resolutely flat two-color surface system. Vivid grass-green (#229f39) and deep cobalt-blue (#2b3386) are sovereign peers: either can fill an entire screen or section, with no neutral intermediary. All text is set in Space Mono at every scale from hero display down to captions, making the monospace grid the primary typographic voice rather than a code aesthetic. All shapes are sharp 90-degree rectangles, and section breaks use stacked parallel horizontal lines as a graphic band.

Mono · Space Mono · 0.75rem

npx shadcn add terrainvivant.json

Aa

Space Mono · Heading

400700

Aa

Space Mono · Body

400700

ABCDEFGHIJKLM NOPQRSTUVWXYZ

abcdefghijklmnopqrstuvwxyz

0123456789 & @ # % →

A complete token set: neutrals biased toward the accent hue, and two families doing different jobs.

Do the people who write it down actually escape the defaults?

Mostly not, and that is the more uncomfortable finding. We sampled 299 public DESIGN.md files from GitHub — the file whose entire job is to stop this — and measured what was in them.

Of the 72 that describe a visual design system, 21% specify a radius scale and 26% specify elevation. Those are two of the four tells above, left at their defaults by three quarters of the people who sat down specifically to write a design contract. 69% omit dark mode entirely, so the whole second theme is left to the agent to invent. And 86% name colours by hue or list bare hexes rather than using semantic roles, which means the file cannot be applied to any component it did not explicitly describe.

The defaults survive the attempt to replace them

Typography is covered in 83% of files and colour in 67%, because those are the two things people think of as 'design'. Radius, elevation and dark mode are the ones that quietly stay generic. The four tells persist not because nobody tried, but because most attempts cover the visible half.

There is a second-order version of the same problem: 54% of files contain at least one vague adjective standing in for a value, led by *clean* (39%) and *modern* (36%). An adjective does not replace a default. It asks the agent to pick one.

A five-minute audit

  1. 1

    Sample the background

    Pull the hex of your page background. If it is #09090b, #18181b, #0f172a or #020617, you are on an untouched zinc or slate ramp.

  2. 2

    Count type families

    Open the computed styles on an H1 and on a body paragraph. Same font-family on both means one family is doing every job.

  3. 3

    Compare two radii

    Measure the corner radius on a button and on your largest card. Identical values mean nobody scaled radius to element size.

  4. 4

    Diff your first and last screen

    Put the earliest screen you built next to the most recent. List every value that disagrees — that list is your drift.

  5. 5

    Decide where the answer lives

    For each disagreement, ask whether the correct value exists anywhere durable. If the honest answer is 'in the prompt I wrote in March', that is the thing to fix.

Skip the four defaults entirely

Every Identity Forge kit ships a full token set — neutrals tuned to the accent, a real font pairing, radius scaled per element — plus the DESIGN.md your agent reads before it writes markup. Free kits need no account.

Is this a problem with the model, or with the tools around it?

Mostly the tools. The same model produces distinctive UI when it is handed a token set and a brief, and generic UI when it is handed neither. What varies is the starting material, not the capability.

Does changing the accent colour fix it?

No, and this is the most common wasted afternoon. Swapping the accent while keeping the default neutrals, single type family and uniform radius produces the same page in a different hue. Type and structure carry more of the identity than the accent does.

Can I just tell the agent to avoid Inter and zinc?

For one session, yes. The instruction lives in context, so it degrades as the conversation grows and disappears entirely when you open a new one. A file in the repo does not have that property.

Is shadcn/ui the problem?

No. shadcn ships sensible defaults precisely so you can start without deciding everything, and its theming layer is what makes the fix straightforward. The problem is that the defaults are treated as finished rather than as a starting point.

How many screens before drift becomes visible?

In our own runs it shows up around the fifth or sixth screen of a continuous session, and much earlier if you switch tools midway. There is no fixed number — it tracks conversation length, not screen count.