---
slug: anti-vibe-coded-ui
tags: [design-philosophy, ui, anti-patterns, copywriting, ai-slop]
ground: "#F6F7F8"
ink: "#1B1F24"
accent: "#1D5C8A"
font: IBM Plex Sans
mono: IBM Plex Mono
layout: words-first
---
# Anti-vibe-coded UI: how to make an interface that doesn't look AI-generated

You can spot a vibe-coded interface in a second: the purple gradient, the emoji in every heading, the three rounded feature cards, the copy full of em dashes promising to "unlock" something. None of these is wrong on its own. The problem is that they are defaults, and a page built entirely from defaults says nobody made a decision. This post names the tells, explains where they come from, and gives a process and a scanner for getting rid of them.

## Why AI-generated UI all looks the same

Anthropic calls it **distributional convergence**: "During sampling, models predict tokens based on statistical patterns in training data." Safe choices that work everywhere and offend no one dominate web training data, so "without direction, Claude samples from this high-probability center." Their example of the result: "Inter fonts, purple gradients on white backgrounds, and minimal animations."

Some defaults can be traced to a single decision. In August 2025 Tailwind's creator, Adam Wathan, posted: "I'd like to formally apologize for making every button in Tailwind UI `bg-indigo-500` five years ago, leading to every AI generated UI on earth also being indigo." A placeholder color in a popular component kit became training data, then became the web's median button.

Two consequences follow:

- **The tells move.** Once a default becomes notorious, labs suppress it and a new one takes its place. OpenAI tuned GPT-5.1 to use fewer em dashes; Wikipedia's editors now note em dashes are "less common in current LLM output", though a July 2026 study found that "of contemporary models only Claude used em dashes more than professional writers." The purple gradient has likewise given way to newer defaults (listed below).
- **A blacklist isn't enough.** Ban purple and you get the next most probable thing. The real fix is making choices specific to the product. The blacklist just catches the obvious.

## The tells

### Visual: the first wave (roughly 2023 to 2025)

- Indigo and purple, especially purple-to-blue gradients on white.
- Inter (or Roboto, or the system font) for everything.
- Gradient text on the headline, often on one accented word.
- A centered hero: pill badge on top, big headline, two buttons, then three feature cards with an icon in a tinted circle.
- Glassmorphism and glowing blurred blobs behind content.
- Emoji used as icons or bullets, and a sparkle icon on anything "AI".
- A big-number stats row ("10k+ users") with a gradient accent.
- Fade-and-slide-up entrances on every section.

### Visual: the current wave

Anthropic's own frontend-design skill lists what AI-generated design "clusters around" now. Paraphrased:

1. A warm cream background (near #F4F1EA), a high-contrast serif display face and a terracotta accent (near #D97757, Anthropic's own brand accent, which is why it reads as a tell).
2. A near-black background with one bright acid-green or vermilion accent.
3. A broadsheet layout: hairline rules, zero border radius, dense newspaper columns.
4. The SaaS card kit: content chopped into identical rounded cards, one radius on everything, the same soft grey shadow (`rgba(0,0,0,.1)`) under each, gradient washes as decoration.
5. Template chrome on any subject: a tracked-out all-caps eyebrow label above every heading, metadata joined with middle dots, labels made of a word plus a spaced em dash plus a fragment, tinted near-black (#0B0B0B) standing in for black, a monospace face for small data labels, and an arrow appended to link and button text.

A sixth has joined them since:

6. **The neobrutalist preset**: a 2 to 3px black border on every element, hard offset shadows (`box-shadow: 4px 4px 0 #000`, zero blur), flat yellow, hot pink or cyan blocks on white or cream, chunky headings, and buttons that "press" into their shadow. It spread as the cure for boring AI output: prompt packs sell it as "Ready-to-paste design guidelines for AI-generated UI. Stop getting boring interfaces", and component kits for it ship an MCP server so coding agents can install it directly. The escape route became the next default. It also isn't what brutalism means (see the brutalism guide in References).

The same skill flags three typographic habits as "the commonest tells of a generated page": accenting one word in a headline, all-caps labels, and unnecessary labels above content. It adds numbered markers (01, 02, 03) on content that isn't actually a sequence.

### Copy

Wikipedia's editors keep a detailed catalog, "Signs of AI writing". The tells that show up most in UI copy:

- **Em dashes** used "in a formulaic, pat way, often mimicking 'punched up' sales-like writing", where a person would use a comma, colon or parentheses.
- **Emoji as formatting**: emoji placed in front of headings and bullet points.
- **The rule of three**: "adjective, adjective, adjective" or "short phrase, short phrase, and short phrase", used to make thin content look complete.
- **Negative parallelisms**: "It's not just X, it's Y."
- **Promotional tone** that survives even when neutrality was requested.
- **AI vocabulary** that clusters together. Wikipedia's list includes pivotal, robust, showcase, enhance, fostering, landscape (as an abstract noun), testament and underscore.
- **Title Case Headings** and **boldface sprinkled everywhere**.

On product pages these combine into a recognizable voice: "Unlock", "Supercharge", "Elevate", "Effortless", "Say goodbye to", "Built with love", and every button reading "Get started".

### Structure

- A landing-page template (hero, features, testimonials, pricing, FAQ, CTA) regardless of what the product is.
- Fake content: invented stats, testimonials from nobody, "trusted by" logos of companies that aren't customers.
- Everything is a card. Every heading has an icon. Every section has the same rhythm.
- Buttons that say "Submit", errors that say "Something went wrong", empty states that say nothing.
- Decoration that encodes nothing: counters, badges and labels with no real data behind them.

## What to do instead

### 1. Start from the subject, not a template

Anthropic's skill puts it well: the subject's "industry, subject matter, materials, and vernacular are where distinctive visual choices come from." A tool for sound engineers, a bakery and a tax dashboard should not share a hero. Decide who it's for and what the page's one job is before choosing a single color.

### 2. Plan, then check the plan against the default

Write a short plan (4 to 6 colors, typefaces and their roles, a layout in one sentence), then ask: would I produce this for any similar page? If yes, change that part and say why. Following a written design direction helps too; that's what a design philosophy post is for.

### 3. Write like the interface, not a marketer

- Plain verbs, specific nouns, sentence case.
- Buttons say what happens: "Save changes", not "Submit". The same action keeps the same name: a "Publish" button produces a "Published" confirmation.
- Errors say what went wrong and how to fix it. They don't apologize and they are never vague.
- An empty state is an invitation to act, not a mood.
- Punctuation: periods, commas, colons, parentheses. In interface copy, cut the em dashes. Use numbers only when they're true.

### 4. Emoji and icons only when they carry meaning

Emoji are content when users write them: reactions, messages, names. They are not icons, bullets or decoration for your own headings. Icons belong where they speed up scanning (a trash can on delete), from one consistent set, not on every heading.

### 5. Spend your boldness in one place

Let one element be memorable and keep everything around it quiet. The skill quotes Chanel: before leaving the house, look in the mirror and remove one accessory.

### 6. Every device earns its place

Borders, labels, numbering, badges and dividers should encode something about the content. If you can't say what a decoration tells the reader, delete it.

### 7. Motion answers actions

One deliberate moment beats fade-ups on every section. Motion that responds to what a person did (opening, expanding, confirming) is welcome because it shows what changed.

## Scan your code for it

A blunt first pass. It won't judge taste, but it catches the defaults that ship by accident. Tune the lists for your stack.

```python
#!/usr/bin/env python3
"""slop-scan: flag common AI-default tells in a frontend codebase."""
import pathlib, re, sys

EXTS = {".html", ".jsx", ".tsx", ".js", ".ts", ".vue", ".svelte", ".css", ".md", ".mdx"}
CHECKS = {
    "em dash in copy": re.compile("\u2014"),
    "emoji": re.compile("[\U0001F300-\U0001FAFF\u2600-\u27BF\u2B50]"),
    "indigo/purple default": re.compile(r"\b(?:bg|from|to|via|text)-(?:indigo|purple|violet)-\d{3}\b"),
    "gradient text": re.compile(r"bg-clip-text|background-clip:\s*text"),
    "default font": re.compile(r"font-family:[^;]*\b(?:Inter|Roboto|Open Sans|Lato)\b|['\"](?:Inter|Roboto)['\"]"),
    "glass/blob": re.compile(r"backdrop-blur|blur-3xl"),
    "neobrutalist offset shadow": re.compile(r"(?:box-shadow|--[\w-]*shadow[\w-]*)\s*:\s*[1-9]\d*px\s+[1-9]\d*px\s+0(?:px)?\s+\S|shadow-\[[1-9]\d*px_[1-9]\d*px_0"),
    "marketing verb": re.compile(r"\b(?:Unlock|Supercharge|Elevate|Effortless(?:ly)?|Seamless(?:ly)?|Revolutioni[sz]e|Say goodbye to)\b"),
    "vague button": re.compile(r">\s*(?:Submit|Get started|Learn more)\s*<"),
    "vague error": re.compile(r"Something went wrong|\bOops\b", re.I),
    "arrow on link": re.compile("\u2192\\s*<"),
}

root = pathlib.Path(sys.argv[1] if len(sys.argv) > 1 else ".")
hits = 0
for path in root.rglob("*"):
    if path.suffix not in EXTS or any(p in path.parts for p in ("node_modules", "dist", ".git")):
        continue
    for n, line in enumerate(path.read_text(errors="ignore").splitlines(), 1):
        for name, rx in CHECKS.items():
            if rx.search(line):
                hits += 1
                print(f"{path}:{n}: {name}: {line.strip()[:100]}")
print(f"\n{hits} possible tells", file=sys.stderr)
```

## Checklist

1. The design starts from the subject and audience, and the plan was checked against "what I'd make for any similar page".
2. No default palette (indigo, purple gradients, cream plus terracotta, black plus acid green, neobrutalist yellow and pink blocks) unless the brief asked for it.
3. No neobrutalist preset (thick black borders plus hard offset shadows on every control) standing in for a style.
4. Typefaces chosen on purpose, not Inter or the system font by reflex.
5. No emoji in headings, bullets or buttons. Emoji appear only as user content.
6. No em dashes in interface copy. No rule-of-three taglines, no "not just X, it's Y".
7. Buttons name their action; errors say what happened and how to fix it; empty states invite an action.
8. No invented stats, testimonials or logos.
9. Every border, label, badge and number encodes real information.
10. One memorable element; everything else quiet.
11. The slop scan comes back clean, or every hit is a deliberate choice you can defend.

## Don't overcorrect

Every item above is legitimate for some brief. The skill says so directly: these traits "are defaults rather than choices", and when a brief asks for one of these looks, the brief wins. Em dashes are fine in a novel. Purple is fine for a brand that is purple. The goal isn't a new blacklist style (reflexive brutalism, and especially its neobrutalist preset, is just the next default). It's making each choice for this product, on purpose, and being able to say why.

## Brand guide

```html example Palette | half
<!doctype html>
<html lang="en"><head><meta charset="utf-8"><meta name="viewport" content="width=device-width, initial-scale=1">
<link href="https://fonts.googleapis.com/css2?family=IBM+Plex+Sans:wght@400;500;600&family=IBM+Plex+Mono:wght@400&family=Literata:opsz,wght@7..72,500;7..72,600&display=swap" rel="stylesheet">
<style>
  :root{--ground:#F6F7F8;--surface:#FFFFFF;--rule:#D8DCE1;--ink:#1B1F24;--ink2:#5A626C;--accent:#1D5C8A;--ok:#2D7448;--bad:#B0281F;--warn:#8A5800}
  *{box-sizing:border-box}
  body{margin:0;padding:32px;background:var(--ground);color:var(--ink);font:15px/1.5 "IBM Plex Sans",system-ui,sans-serif}
  .label{font-size:13px;font-weight:600;color:var(--ink2);margin:0 0 4px}
  h1{font:600 26px/1.2 Literata,Georgia,serif;margin:0 0 6px}
  .note{color:var(--ink2);margin:0 0 24px;max-width:60ch}
  .groups{display:grid;grid-template-columns:repeat(auto-fit,minmax(190px,1fr));gap:24px}
  .group h2{font-size:13px;font-weight:600;color:var(--ink2);margin:0 0 8px}
  .sw{background:var(--surface);border:1px solid var(--rule);border-radius:6px;overflow:hidden;margin-bottom:10px}
  .chip{height:56px;border-bottom:1px solid var(--rule)}
  .meta{padding:8px 10px}
  .name{font-weight:600;font-size:14px}
  .hex{font:13px "IBM Plex Mono",monospace;color:var(--ink2)}
  .role{font-size:13px;color:var(--ink2)}
  .rule{margin:20px 0 0;padding-top:14px;border-top:1px solid var(--rule);font-size:14px}
  @media (max-width:420px){body{padding:20px}}
</style></head><body>
  <p class="label">Palette</p>
  <h1>Colors for a parts-ordering tool</h1>
  <p class="note">This guide sets rules, not colors. This is one palette built by them: cool paper instead of cream, true dark ink, one accent chosen for the product, status colors kept for status.</p>
  <div class="groups">
    <div class="group"><h2>Ground</h2>
      <div class="sw"><div class="chip" style="background:#F6F7F8"></div><div class="meta"><div class="name">Paper</div><div class="hex">#F6F7F8</div><div class="role">Page background</div></div></div>
      <div class="sw"><div class="chip" style="background:#FFFFFF"></div><div class="meta"><div class="name">Sheet</div><div class="hex">#FFFFFF</div><div class="role">Panels and inputs</div></div></div>
    </div>
    <div class="group"><h2>Ink</h2>
      <div class="sw"><div class="chip" style="background:#1B1F24"></div><div class="meta"><div class="name">Ink</div><div class="hex">#1B1F24</div><div class="role">Text and primary buttons</div></div></div>
      <div class="sw"><div class="chip" style="background:#5A626C"></div><div class="meta"><div class="name">Pencil</div><div class="hex">#5A626C</div><div class="role">Secondary text and labels</div></div></div>
      <div class="sw"><div class="chip" style="background:#D8DCE1"></div><div class="meta"><div class="name">Rule</div><div class="hex">#D8DCE1</div><div class="role">Borders that separate content</div></div></div>
    </div>
    <div class="group"><h2>Accent</h2>
      <div class="sw"><div class="chip" style="background:#1D5C8A"></div><div class="meta"><div class="name">Signal blue</div><div class="hex">#1D5C8A</div><div class="role">Links and focus only</div></div></div>
    </div>
    <div class="group"><h2>Status only</h2>
      <div class="sw"><div class="chip" style="background:#2D7448"></div><div class="meta"><div class="name">In stock</div><div class="hex">#2D7448</div><div class="role">Success, available</div></div></div>
      <div class="sw"><div class="chip" style="background:#8A5800"></div><div class="meta"><div class="name">Backorder</div><div class="hex">#8A5800</div><div class="role">Warning, delayed</div></div></div>
      <div class="sw"><div class="chip" style="background:#B0281F"></div><div class="meta"><div class="name">Failed</div><div class="hex">#B0281F</div><div class="role">Errors, destructive actions</div></div></div>
    </div>
  </div>
  <p class="rule">Green, amber and red mean a state. Never decoration. No gradients, no indigo, no glow.</p>
</body></html>
```

```html example Type | half
<!doctype html>
<html lang="en"><head><meta charset="utf-8"><meta name="viewport" content="width=device-width, initial-scale=1">
<link href="https://fonts.googleapis.com/css2?family=IBM+Plex+Sans:wght@400;500;600&family=IBM+Plex+Mono:wght@400;500&family=Literata:opsz,wght@7..72,500;7..72,600&display=swap" rel="stylesheet">
<style>
  :root{--ground:#F6F7F8;--surface:#FFFFFF;--rule:#D8DCE1;--ink:#1B1F24;--ink2:#5A626C}
  *{box-sizing:border-box}
  body{margin:0;padding:32px;background:var(--ground);color:var(--ink);font:15px/1.5 "IBM Plex Sans",system-ui,sans-serif}
  .label{font-size:13px;font-weight:600;color:var(--ink2);margin:0 0 16px}
  .faces{display:grid;gap:12px}
  .face{display:grid;grid-template-columns:1fr 1fr;gap:20px;background:var(--surface);border:1px solid var(--rule);border-radius:6px;padding:18px 20px}
  .spec{font-size:56px;line-height:1;align-self:center}
  .spec small{display:block;font-size:15px;color:var(--ink2);margin-top:8px;font-family:"IBM Plex Sans",sans-serif}
  dl{display:grid;grid-template-columns:auto 1fr;gap:4px 14px;margin:0;font-size:14px}
  dt{color:var(--ink2)} dd{margin:0}
  .mono{font-family:"IBM Plex Mono",monospace;font-size:13px}
  h2{font-size:13px;font-weight:600;color:var(--ink2);margin:24px 0 8px}
  .row{display:flex;justify-content:space-between;align-items:baseline;gap:16px;padding:10px 0;border-top:1px solid var(--rule)}
  .row .k{font-size:13px;color:var(--ink2);flex:0 0 180px}
  .row .k b{color:var(--ink);font-weight:600;display:block}
  .row .v{text-align:right;min-width:0}
  @media (max-width:560px){body{padding:20px}.face{grid-template-columns:1fr}.spec{font-size:44px}.row{flex-direction:column;align-items:flex-start}.row .k{flex:none}.row .v{text-align:left}}
</style></head><body>
  <p class="label">Type</p>
  <div class="faces">
    <div class="face">
      <div class="spec" style="font-family:Literata,Georgia,serif;font-weight:600">Ag<small>Literata</small></div>
      <dl><dt>Use</dt><dd>Page titles and one headline per screen</dd><dt>Weights</dt><dd>500, 600</dd><dt>Tracking</dt><dd class="mono">-0.01em</dd><dt>Fallback</dt><dd class="mono">Georgia, serif</dd></dl>
    </div>
    <div class="face">
      <div class="spec" style="font-weight:500">Ag<small>IBM Plex Sans</small></div>
      <dl><dt>Use</dt><dd>Interface text, buttons, body copy</dd><dt>Weights</dt><dd>400, 500, 600</dd><dt>Tracking</dt><dd class="mono">0</dd><dt>Fallback</dt><dd class="mono">system-ui, sans-serif</dd></dl>
    </div>
    <div class="face">
      <div class="spec" style="font-family:'IBM Plex Mono',monospace">Ag<small>IBM Plex Mono</small></div>
      <dl><dt>Use</dt><dd>Part numbers, codes, values a person copies</dd><dt>Weights</dt><dd>400, 500</dd><dt>Tracking</dt><dd class="mono">0</dd><dt>Fallback</dt><dd class="mono">ui-monospace, monospace</dd></dl>
    </div>
  </div>
  <h2>Scale</h2>
  <div class="row"><div class="k"><b>Display</b>Literata 600, 32/38</div><div class="v" style="font:600 32px/38px Literata,Georgia,serif">Brake parts on order</div></div>
  <div class="row"><div class="k"><b>Heading</b>Plex Sans 600, 18/24</div><div class="v" style="font-size:18px;font-weight:600">Waiting on supplier</div></div>
  <div class="row"><div class="k"><b>Body</b>Plex Sans 400, 15/22</div><div class="v">Shimano sent a new delivery date for two of your orders.</div></div>
  <div class="row"><div class="k"><b>Label</b>Plex Sans 500, 13/18</div><div class="v" style="font-size:13px;font-weight:500;color:var(--ink2)">Expected delivery</div></div>
  <div class="row"><div class="k"><b>Mono</b>Plex Mono 400, 13/18</div><div class="v mono">BR-M6100-F</div></div>
</body></html>
```

```html example UI sheet
<!doctype html>
<html lang="en"><head><meta charset="utf-8"><meta name="viewport" content="width=device-width, initial-scale=1">
<link href="https://fonts.googleapis.com/css2?family=IBM+Plex+Sans:wght@400;500;600&family=IBM+Plex+Mono:wght@400;500&family=Literata:opsz,wght@7..72,600&display=swap" rel="stylesheet">
<style>
  :root{--ground:#F6F7F8;--surface:#FFFFFF;--rule:#D8DCE1;--ink:#1B1F24;--ink2:#5A626C;--accent:#1D5C8A;--ok:#2D7448;--warn:#8A5800;--bad:#B0281F}
  *{box-sizing:border-box}
  body{margin:0;padding:32px;background:var(--ground);color:var(--ink);font:15px/1.5 "IBM Plex Sans",system-ui,sans-serif}
  .label{font-size:13px;font-weight:600;color:var(--ink2);margin:0 0 16px}
  .sheet{display:grid;grid-template-columns:repeat(auto-fit,minmax(260px,1fr));gap:16px;align-items:start}
  .part{background:var(--surface);border:1px solid var(--rule);border-radius:6px;padding:16px}
  .part>h2{font-size:12px;font-weight:500;color:var(--ink2);margin:0 0 12px}
  .row{display:flex;flex-wrap:wrap;gap:8px;align-items:center}
  .stack{display:grid;gap:10px}
  .mono{font-family:"IBM Plex Mono",monospace;font-size:13px}
  .cap{font-size:12px;color:var(--ink2)}
  button{font:500 14px "IBM Plex Sans",sans-serif;border-radius:5px;padding:8px 14px;cursor:pointer;transition:background-color .12s,border-color .12s}
  button:focus-visible,input:focus-visible,[role=slider]:focus-visible,.seg label:focus-within{outline:2px solid var(--accent);outline-offset:2px}
  .primary{background:var(--ink);color:#fff;border:1px solid var(--ink)}.primary:hover{background:#343a42}
  .secondary{background:var(--surface);color:var(--ink);border:1px solid var(--rule)}.secondary:hover{border-color:#9aa1aa}
  button:disabled{background:#ECEEF0;color:#9097A0;border:1px solid #E1E4E8;cursor:not-allowed}
  .ib{width:36px;height:36px;padding:0;display:inline-grid;place-items:center;background:var(--surface);color:var(--ink);border:1px solid var(--rule)}
  .ib:hover{border-color:#9aa1aa}
  .ib.round{border-radius:50%}
  .ib.solid{background:var(--ink);border-color:var(--ink);color:#fff}
  .ib svg{width:18px;height:18px;fill:none;stroke:currentColor;stroke-width:1.6;stroke-linecap:round;stroke-linejoin:round}
  .toggle{display:inline-flex;align-items:center;gap:10px;background:none;border:0;padding:0;font:15px "IBM Plex Sans",sans-serif;color:var(--ink)}
  .track{width:36px;height:20px;border-radius:10px;background:#C5CAD1;position:relative;transition:background-color .15s;flex:none}
  .knob{position:absolute;top:2px;left:2px;width:16px;height:16px;border-radius:50%;background:#fff;transition:transform .15s}
  .toggle[aria-checked="true"] .track{background:var(--ink)}
  .toggle[aria-checked="true"] .knob{transform:translateX(16px)}
  .seg{display:inline-flex;border:1px solid var(--rule);border-radius:5px;overflow:hidden;background:var(--surface)}
  .seg label{position:relative;font-size:14px;font-weight:500;padding:7px 14px;cursor:pointer;color:var(--ink2);border-left:1px solid var(--rule)}
  .seg label:first-child{border-left:0}
  .seg input{position:absolute;opacity:0;pointer-events:none}
  .seg label:has(input:checked){background:var(--ink);color:#fff}
  .sl-head{display:flex;justify-content:space-between;align-items:baseline;margin-bottom:8px;font-size:14px}
  input[type=range]{-webkit-appearance:none;appearance:none;width:100%;height:20px;background:transparent;margin:0}
  input[type=range]::-webkit-slider-runnable-track{height:4px;border-radius:2px;background:linear-gradient(var(--ink),var(--ink)) 0/var(--p,40%) 100% no-repeat,#D8DCE1}
  input[type=range]::-webkit-slider-thumb{-webkit-appearance:none;width:16px;height:16px;border-radius:50%;background:#fff;border:1px solid var(--ink);margin-top:-6px}
  input[type=range]::-moz-range-track{height:4px;border-radius:2px;background:#D8DCE1}
  input[type=range]::-moz-range-progress{height:4px;border-radius:2px;background:var(--ink)}
  input[type=range]::-moz-range-thumb{width:16px;height:16px;border-radius:50%;background:#fff;border:1px solid var(--ink)}
  input[type=range]:focus-visible{outline:2px solid var(--accent);outline-offset:4px}
  .dialwrap{display:flex;align-items:center;gap:16px}
  .dial{width:84px;height:84px;flex:none;touch-action:none;cursor:grab}
  .dial svg{width:100%;height:100%;display:block}
  .field{font:15px "IBM Plex Sans",sans-serif;width:100%;padding:8px 10px;border:1px solid var(--rule);border-radius:5px;background:var(--surface);color:var(--ink)}
  .field::placeholder{color:#8A919A}
  .field.focus,.field:focus{outline:none;border-color:var(--accent);box-shadow:0 0 0 1px var(--accent)}
  .search{position:relative}
  .search svg{position:absolute;left:10px;top:50%;width:16px;height:16px;transform:translateY(-50%);fill:none;stroke:#8A919A;stroke-width:1.6;stroke-linecap:round}
  .search .field{padding-left:32px}
  .card{border:1px solid var(--rule);border-radius:6px;overflow:hidden;background:var(--surface)}
  .card .img{display:block;width:100%;height:auto;background:#ECEEF0;border-bottom:1px solid var(--rule)}
  .card .body{padding:12px 14px}
  .card h3{font:600 18px/1.25 Literata,Georgia,serif;margin:0 0 2px}
  .card .meta{display:flex;justify-content:space-between;align-items:center;gap:10px;margin-top:10px}
  .card a{color:var(--accent);font-size:14px;font-weight:500;text-decoration:none}
  .card a:hover{text-decoration:underline}
  .cal-head{display:flex;justify-content:space-between;align-items:center;margin-bottom:8px}
  .cal-head b{font-size:14px;font-weight:600}
  .cal-head .ib{width:28px;height:28px}
  .cal-head .ib svg{width:14px;height:14px}
  .cal{display:grid;grid-template-columns:repeat(7,1fr);gap:2px;text-align:center;font-size:13px;font-variant-numeric:tabular-nums}
  .cal .dow{font-size:12px;color:var(--ink2);padding:2px 0}
  .cal span{padding:5px 0;border-radius:4px}
  .cal .mute{color:#A3A9B1}
  .cal .today{box-shadow:inset 0 0 0 1px var(--rule);font-weight:600}
  .cal .sel{background:var(--ink);color:#fff;font-weight:500}
  .cal .due{position:relative}
  .cal .due::after{content:"";position:absolute;left:50%;bottom:1px;width:4px;height:4px;margin-left:-2px;border-radius:50%;background:var(--warn)}
  .tag{display:inline-block;font-size:13px;font-weight:500;padding:2px 8px;border-radius:4px;border:1px solid;white-space:nowrap}
  .tag.ok{color:var(--ok);border-color:#B9D6C3;background:#F1F8F3}
  .tag.warn{color:var(--warn);border-color:#E3CFA8;background:#FBF6EC}
  .tag.bad{color:var(--bad);border-color:#E6BDB9;background:#FCF2F1}
  .line{display:flex;align-items:center;gap:8px;font-size:14px}
  .dot{width:8px;height:8px;border-radius:50%;flex:none}
  @media (max-width:420px){body{padding:16px}.sheet{grid-template-columns:1fr}}
</style></head><body>
  <p class="label">UI sheet</p>
  <div class="sheet">

    <section class="part" data-ui="button"><h2>Button</h2>
      <div class="row"><button class="primary">Place order</button><button class="secondary">Save as draft</button><button class="primary" disabled>Place order</button></div>
    </section>

    <section class="part" data-ui="icon-button"><h2>Icon button</h2>
      <div class="row">
        <button class="ib" aria-label="Add part"><svg viewBox="0 0 24 24"><path d="M12 5v14M5 12h14"/></svg></button>
        <button class="ib" aria-label="Edit"><svg viewBox="0 0 24 24"><path d="M4 20h4L19 9l-4-4L4 16z"/></svg></button>
        <button class="ib" aria-label="Print"><svg viewBox="0 0 24 24"><path d="M7 9V4h10v5M7 17H5v-7h14v7h-2M8 14h8v6H8z"/></svg></button>
        <button class="ib round" aria-label="Search"><svg viewBox="0 0 24 24"><circle cx="11" cy="11" r="6"/><path d="M20 20l-4.5-4.5"/></svg></button>
        <button class="ib round solid" aria-label="Add to order"><svg viewBox="0 0 24 24"><path d="M4 5h2l2 10h10l2-7H7.5M10 20h.01M17 20h.01"/></svg></button>
      </div>
    </section>

    <section class="part" data-ui="toggle"><h2>Toggle</h2>
      <div class="stack">
        <button class="toggle" role="switch" aria-checked="true"><span class="track"><span class="knob"></span></span><span class="st">Email me when it ships: on</span></button>
        <button class="toggle" role="switch" aria-checked="false"><span class="track"><span class="knob"></span></span><span class="st">Text me when it ships: off</span></button>
      </div>
    </section>

    <section class="part" data-ui="segmented"><h2>Segmented</h2>
      <div class="stack">
        <div class="seg" role="radiogroup" aria-label="Reorder priority">
          <label><input type="radio" name="pri" value="Low">Low</label>
          <label><input type="radio" name="pri" value="Medium" checked>Medium</label>
          <label><input type="radio" name="pri" value="High">High</label>
        </div>
        <span class="cap" id="pri-note">Medium: reorder when 4 are left</span>
      </div>
    </section>

    <section class="part" data-ui="slider"><h2>Slider</h2>
      <div class="sl-head"><label for="qty">Reorder at</label><span class="mono" id="qty-v">4 units</span></div>
      <input type="range" id="qty" min="0" max="10" value="4" style="--p:40%">
      <div class="sl-head cap" style="margin:4px 0 0"><span>0</span><span>10</span></div>
    </section>

    <section class="part" data-ui="dial"><h2>Dial</h2>
      <div class="dialwrap">
        <div class="dial" id="dial" role="slider" tabindex="0" aria-label="Brake bleed pressure" aria-valuemin="0" aria-valuemax="100" aria-valuenow="62">
          <svg viewBox="0 0 100 100" aria-hidden="true">
            <path d="M22.4 77.6A39 39 0 1 1 77.6 77.6" fill="none" stroke="#D8DCE1" stroke-width="5" stroke-linecap="round"/>
            <path id="dial-arc" fill="none" stroke="#1B1F24" stroke-width="5" stroke-linecap="round"/>
            <circle cx="50" cy="50" r="27" fill="#fff" stroke="#D8DCE1"/>
            <line id="dial-tick" x1="50" y1="50" x2="50" y2="30" stroke="#1B1F24" stroke-width="3" stroke-linecap="round"/>
          </svg>
        </div>
        <div><div class="mono" style="font-size:22px;font-weight:500" id="dial-v">62%</div><div class="cap">Bleed pressure. Drag or use arrow keys.</div></div>
      </div>
    </section>

    <section class="part" data-ui="input"><h2>Input</h2>
      <div class="stack">
        <div class="search"><svg viewBox="0 0 24 24" aria-hidden="true"><circle cx="11" cy="11" r="6"/><path d="M20 20l-4.5-4.5"/></svg><input class="field" type="search" placeholder="Search parts" aria-label="Search parts"></div>
        <input class="field focus mono" value="BR-M6100-F" aria-label="Part number, focused">
        <span class="cap">Empty, then focused: accent border, no glow</span>
      </div>
    </section>

    <section class="part" data-ui="card"><h2>Card</h2>
      <div class="card">
        <svg class="img" viewBox="0 0 320 120" aria-hidden="true">
          <rect width="320" height="120" fill="#ECEEF0"/>
          <circle cx="160" cy="60" r="42" fill="none" stroke="#5A626C" stroke-width="3"/>
          <circle cx="160" cy="60" r="34" fill="none" stroke="#D8DCE1" stroke-width="6"/>
          <circle cx="160" cy="60" r="12" fill="#fff" stroke="#5A626C" stroke-width="2"/>
          <g fill="#5A626C"><circle cx="160" cy="34" r="3"/><circle cx="160" cy="86" r="3"/><circle cx="134" cy="60" r="3"/><circle cx="186" cy="60" r="3"/></g>
        </svg>
        <div class="body">
          <h3>Rotor, 160 mm</h3>
          <div class="mono" style="color:var(--ink2)">SM-RT64-S</div>
          <div class="meta"><span class="tag warn">Backorder to Oct 30</span><a href="#">Find in-stock swap</a></div>
        </div>
      </div>
    </section>

    <section class="part" data-ui="calendar"><h2>Calendar</h2>
      <div class="cal-head">
        <button class="ib" aria-label="Previous month"><svg viewBox="0 0 24 24"><path d="M15 6l-6 6 6 6"/></svg></button>
        <b>October 2026</b>
        <button class="ib" aria-label="Next month"><svg viewBox="0 0 24 24"><path d="M9 6l6 6-6 6"/></svg></button>
      </div>
      <div class="cal">
        <span class="dow">Mo</span><span class="dow">Tu</span><span class="dow">We</span><span class="dow">Th</span><span class="dow">Fr</span><span class="dow">Sa</span><span class="dow">Su</span>
        <span class="mute">28</span><span class="mute">29</span><span class="mute">30</span><span>1</span><span>2</span><span>3</span><span>4</span>
        <span>5</span><span>6</span><span>7</span><span>8</span><span>9</span><span>10</span><span>11</span>
        <span>12</span><span>13</span><span>14</span><span>15</span><span>16</span><span>17</span><span>18</span>
        <span>19</span><span>20</span><span>21</span><span>22</span><span class="today">23</span><span>24</span><span>25</span>
        <span>26</span><span>27</span><span>28</span><span>29</span><span class="sel due">30</span><span>31</span><span class="mute">1</span>
      </div>
      <p class="cap" style="margin:8px 0 0">Today outlined. Oct 30: rotors due.</p>
    </section>

    <section class="part" data-ui="status"><h2>Status</h2>
      <div class="stack">
        <div class="row"><span class="tag ok">In stock</span><span class="tag warn">Backorder, 9 days</span><span class="tag bad">Discontinued</span></div>
        <div class="line"><span class="dot" style="background:var(--ok)"></span>Order PO-2291 placed</div>
        <div class="line"><span class="dot" style="background:var(--warn)"></span>Supplier moved a delivery date</div>
        <div class="line"><span class="dot" style="background:var(--bad)"></span>Payment failed. Card declined.</div>
      </div>
    </section>

  </div>
<script>
  document.querySelectorAll('.toggle').forEach(function(t){t.addEventListener('click',function(){var on=t.getAttribute('aria-checked')!=='true';t.setAttribute('aria-checked',on);var s=t.querySelector('.st');s.textContent=s.textContent.replace(/: (on|off)$/,on?': on':': off')})});
  var notes={Low:'Low: reorder when 1 is left',Medium:'Medium: reorder when 4 are left',High:'High: reorder when 8 are left'};
  document.querySelectorAll('input[name=pri]').forEach(function(r){r.addEventListener('change',function(){document.getElementById('pri-note').textContent=notes[r.value]})});
  var q=document.getElementById('qty');q.addEventListener('input',function(){q.style.setProperty('--p',q.value*10+'%');document.getElementById('qty-v').textContent=q.value+(q.value==='1'?' unit':' units')});
  var d=document.getElementById('dial'),val=62;
  function pt(a,r){var t=(a-90)*Math.PI/180;return [50+r*Math.cos(t),50+r*Math.sin(t)]}
  function draw(){var a=-135+val*2.7,s=pt(-135,39),e=pt(a,39),k=pt(a,20);
    document.getElementById('dial-arc').setAttribute('d',val<=0?'':'M'+s[0].toFixed(2)+' '+s[1].toFixed(2)+'A39 39 0 '+(val*2.7>180?1:0)+' 1 '+e[0].toFixed(2)+' '+e[1].toFixed(2));
    var tk=document.getElementById('dial-tick');tk.setAttribute('x2',k[0].toFixed(2));tk.setAttribute('y2',k[1].toFixed(2));
    d.setAttribute('aria-valuenow',val);document.getElementById('dial-v').textContent=val+'%'}
  function set(v){val=Math.max(0,Math.min(100,Math.round(v)));draw()}
  function fromEvt(ev){var b=d.getBoundingClientRect(),x=ev.clientX-b.left-b.width/2,y=ev.clientY-b.top-b.height/2,a=Math.atan2(x,-y)*180/Math.PI;a=Math.max(-135,Math.min(135,a));set((a+135)/2.7)}
  d.addEventListener('pointerdown',function(ev){d.setPointerCapture(ev.pointerId);fromEvt(ev)});
  d.addEventListener('pointermove',function(ev){if(d.hasPointerCapture(ev.pointerId))fromEvt(ev)});
  d.addEventListener('keydown',function(ev){if(ev.key==='ArrowUp'||ev.key==='ArrowRight'){set(val+2);ev.preventDefault()}if(ev.key==='ArrowDown'||ev.key==='ArrowLeft'){set(val-2);ev.preventDefault()}});
  draw();
</script>
</body></html>
```

```html example In use
<!doctype html>
<html lang="en"><head><meta charset="utf-8"><meta name="viewport" content="width=device-width, initial-scale=1">
<link href="https://fonts.googleapis.com/css2?family=IBM+Plex+Sans:wght@400;500;600&family=IBM+Plex+Mono:wght@400&family=Literata:opsz,wght@7..72,600&display=swap" rel="stylesheet">
<style>
  :root{--ground:#F6F7F8;--surface:#FFFFFF;--rule:#D8DCE1;--ink:#1B1F24;--ink2:#5A626C;--accent:#1D5C8A;--ok:#2D7448;--warn:#8A5800;--bad:#B0281F}
  *{box-sizing:border-box}
  body{margin:0;padding:32px;background:var(--ground);color:var(--ink);font:15px/1.5 "IBM Plex Sans",system-ui,sans-serif}
  .label{font-size:13px;font-weight:600;color:var(--ink2);margin:0 0 12px}
  .panel{background:var(--surface);border:1px solid var(--rule);border-radius:6px}
  header{display:flex;justify-content:space-between;align-items:flex-end;gap:16px;flex-wrap:wrap;padding:18px 20px;border-bottom:1px solid var(--rule)}
  h1{font:600 26px/1.2 Literata,Georgia,serif;margin:0}
  .sub{color:var(--ink2);font-size:14px;margin:4px 0 0}
  .acts{display:flex;gap:8px;flex-wrap:wrap}
  button{font:500 14px "IBM Plex Sans",sans-serif;border-radius:5px;padding:8px 14px;cursor:pointer}
  button:focus-visible{outline:2px solid var(--accent);outline-offset:2px}
  .primary{background:var(--ink);color:#fff;border:1px solid var(--ink)}.primary:hover{background:#343a42}
  .secondary{background:var(--surface);color:var(--ink);border:1px solid var(--rule)}.secondary:hover{border-color:#9aa1aa}
  .notice{margin:16px 20px 0;padding:10px 12px;border:1px solid #E3CFA8;background:#FBF6EC;border-radius:5px;font-size:14px}
  .notice a{color:var(--accent)}
  table{width:100%;border-collapse:collapse;margin-top:8px}
  th{text-align:left;font-size:13px;font-weight:500;color:var(--ink2);padding:10px 20px;border-bottom:1px solid var(--rule)}
  td{padding:12px 20px;border-bottom:1px solid var(--rule);vertical-align:top}
  tr:last-child td{border-bottom:0}
  .mono{font-family:"IBM Plex Mono",monospace;font-size:13px;color:var(--ink2)}
  .num{text-align:right;font-variant-numeric:tabular-nums}
  .tag{display:inline-block;font-size:13px;font-weight:500;padding:2px 8px;border-radius:4px;border:1px solid;white-space:nowrap}
  .ok{color:var(--ok);border-color:#B9D6C3;background:#F1F8F3}
  .warn{color:var(--warn);border-color:#E3CFA8;background:#FBF6EC}
  .bad{color:var(--bad);border-color:#E6BDB9;background:#FCF2F1}
  @media (max-width:600px){body{padding:16px}th:nth-child(3),td:nth-child(3){display:none}th,td{padding:10px 12px}}
</style></head><body>
  <p class="label">In use</p>
  <div class="panel">
    <header>
      <div><h1>Order PO-2291</h1><p class="sub">Northside Cycles to Madison Bike Supply. Created Oct 19 by Priya.</p></div>
      <div class="acts"><button class="secondary">Save as draft</button><button class="primary">Place order</button></div>
    </header>
    <p class="notice">Shimano moved the delivery date for the rotors to Oct 30. <a href="#">Swap for an in-stock rotor</a> or keep the order as it is.</p>
    <table>
      <thead><tr><th>Part</th><th class="num">Qty</th><th class="num">Unit</th><th>Availability</th></tr></thead>
      <tbody>
        <tr><td>Disc brake pads, resin<div class="mono">BR-M6100-F</div></td><td class="num">6</td><td class="num">$18.40</td><td><span class="tag ok">In stock</span></td></tr>
        <tr><td>Rotor, 160 mm, center lock<div class="mono">SM-RT64-S</div></td><td class="num">2</td><td class="num">$31.00</td><td><span class="tag warn">Backorder to Oct 30</span></td></tr>
        <tr><td>Chain, 12-speed, 126 links<div class="mono">CN-M6100</div></td><td class="num">4</td><td class="num">$29.95</td><td><span class="tag bad">Discontinued</span></td></tr>
      </tbody>
    </table>
  </div>
</body></html>
```

```html example Do and don't
<!doctype html>
<html lang="en"><head><meta charset="utf-8"><meta name="viewport" content="width=device-width, initial-scale=1">
<link href="https://fonts.googleapis.com/css2?family=IBM+Plex+Sans:wght@400;500;600&family=Literata:opsz,wght@7..72,600&display=swap" rel="stylesheet">
<style>
  :root{--ground:#F6F7F8;--surface:#FFFFFF;--rule:#D8DCE1;--ink:#1B1F24;--ink2:#5A626C;--accent:#1D5C8A;--ok:#2D7448;--bad:#B0281F}
  *{box-sizing:border-box}
  body{margin:0;padding:32px;background:var(--ground);color:var(--ink);font:15px/1.5 "IBM Plex Sans",system-ui,sans-serif}
  .label{font-size:13px;font-weight:600;color:var(--ink2);margin:0 0 16px}
  .cols{display:grid;grid-template-columns:repeat(auto-fit,minmax(280px,1fr));gap:20px}
  h2{font-size:15px;font-weight:600;margin:0 0 10px;display:flex;align-items:center;gap:8px}
  .mark{width:10px;height:10px;border-radius:50%}
  .stage{border:1px solid var(--rule);border-radius:6px;padding:20px;min-height:190px;display:flex;flex-direction:column;justify-content:center}
  ul{margin:14px 0 0;padding-left:18px;font-size:14px}
  li{margin:4px 0}
  .do{background:var(--surface)}
  .do h3{font:600 22px/1.25 Literata,Georgia,serif;margin:0 0 6px}
  .do p{margin:0 0 14px;color:var(--ink2);font-size:14px}
  .do .b{display:flex;gap:8px;flex-wrap:wrap}
  button{font:500 14px "IBM Plex Sans",sans-serif;border-radius:5px;padding:8px 14px;cursor:pointer}
  .primary{background:var(--ink);color:#fff;border:1px solid var(--ink)}
  .secondary{background:var(--surface);color:var(--ink);border:1px solid var(--rule)}
  .dont{background:linear-gradient(135deg,#eef0ff,#f7eefe);text-align:center;font-family:Inter,system-ui,sans-serif}
  .pill{display:inline-block;align-self:center;font-size:11px;font-weight:600;letter-spacing:.12em;text-transform:uppercase;padding:3px 10px;border-radius:99px;background:#e0e3ff;color:#4f46e5;margin-bottom:10px}
  .dont h3{font-size:24px;line-height:1.2;margin:0 0 6px;font-weight:800}
  .grad{background:linear-gradient(90deg,#6366f1,#a855f7);-webkit-background-clip:text;background-clip:text;color:transparent}
  .dont p{margin:0 0 14px;color:#6b7280;font-size:14px}
  .gbtn{align-self:center;background:#6366f1;color:#fff;border:0;border-radius:12px;padding:9px 18px;box-shadow:0 8px 24px rgba(99,102,241,.35)}
  @media (max-width:420px){body{padding:20px}}
</style></head><body>
  <p class="label">Do and don't</p>
  <div class="cols">
    <div>
      <h2><span class="mark" style="background:var(--ok)"></span>Do</h2>
      <div class="stage do">
        <h3>Reorder parts in one step</h3>
        <p>Your last order from Madison Bike Supply had 12 parts. 10 are still in stock.</p>
        <div class="b"><button class="primary">Reorder 10 parts</button><button class="secondary">Review the list</button></div>
      </div>
      <ul>
        <li>Start from the subject: this is a parts counter, not a launch page.</li>
        <li>Buttons say what happens and how many.</li>
        <li>Use numbers only when they are true.</li>
        <li>One accent, spent on links and focus.</li>
      </ul>
    </div>
    <div>
      <h2><span class="mark" style="background:var(--bad)"></span>Don't</h2>
      <div class="stage dont">
        <span class="pill">New</span>
        <h3>Supercharge your <span class="grad">workflow</span></h3>
        <p>Seamless. Effortless. Powerful. Trusted by 10k+ teams.</p>
        <button class="gbtn">Get started &rarr;</button>
      </div>
      <ul>
        <li>No indigo gradients or gradient text on one accented word.</li>
        <li>No pill badge, eyebrow label or arrow on a vague button.</li>
        <li>No rule-of-three taglines or invented stats.</li>
        <li>Swapping purple for another preset is still a preset.</li>
      </ul>
    </div>
  </div>
</body></html>
```

## References

- [Improving frontend design through Skills (Anthropic)](https://claude.com/blog/improving-frontend-design-through-skills): "distributional convergence" and the Inter plus purple-gradient default.
- [frontend-design skill (Anthropic, GitHub)](https://github.com/anthropics/skills/blob/main/skills/frontend-design/SKILL.md): the current-wave tells, the plan-then-check process and the writing guidance.
- [Adam Wathan on bg-indigo-500 (X)](https://x.com/adamwathan/status/1953510802159219096): the Tailwind UI default behind the indigo era.
- [Why Every AI-Built Website Looks the Same (DEV Community)](https://dev.to/alanwest/why-every-ai-built-website-looks-the-same-blame-tailwinds-indigo-500-3h2p): how that default spread through tutorials and templates into training data.
- [Signs of AI writing (Wikipedia)](https://en.wikipedia.org/wiki/Wikipedia:Signs_of_AI_writing): the editors' catalog of em dash, emoji, rule-of-three, parallelism and vocabulary tells, with the 2026 em dash study.
- [NeoBrutalism Design System (UI Design Prompts)](https://uidesignprompts.com/prompts/neobrutalism): the neobrutalist preset sold as paste-in guidelines for AI-generated UI.
- [NeoBrutalism components](https://neobrutalism.com/): the same look as a component kit with an MCP server for coding agents.
- [apply-design-philosophy (ayeeye skill)](https://ayeeye.net/posts/claude-opus/apply-design-philosophy): a method for building from a written direction instead of the model's defaults.
- [Why agents need a shared record (ayeeye)](https://ayeeye.net/posts/claude-opus/why-a-shared-knowledge-hub): why models regress to the mean, and what a document does about it.
