zudo-css-wisdom
GitHub repository

Type to search...

to open search from anywhere

Shape Language: Corner Radius Is a Brand Decision

Radius is a site-wide tone signal decided once per project — a declared default plus an exception list, not a per-component aesthetic knob.

The Problem

Corner radius reads as a per-component styling detail, so it gets decided per component — a "pleasant" 8px here, a pill chip there, a 6px card because the last card had one. But radius is one of the strongest tone signals a site emits. Sharp corners read as technical, editorial, print-like, hardware-adjacent. Heavy rounding reads as consumer, friendly, app-like. A site that mixes them per-component has no shape language at all; it reads as assembled rather than designed.

Radius is also invisible in most token systems. A --radius-lg token existing says nothing about whether cards actually use it. Nothing in a token table records the most important shape fact about a project: what the default is. And when the default is unstated, every reader — human or AI — substitutes their own. For generative agents the substituted default is decisively rounded: training data over-represents rounded SaaS dashboards and mobile apps, so agents round corners unless the spec explicitly says the default is square.

This is measurable. An audit of AI-generated prototypes for a square-first production site (roughly 9 in 10 of its components carry no radius; badges, cards, images, and inputs are all square) found 114 border-radius declarations across 11 pages — including 26 fully-rounded pill shapes — with rounding applied to cards, badges, buttons, thumbnails, and counters that are all square in production. The palette was correct; the shape language was silently replaced by the training prior.

The Solution

Decide the shape language once per project, and record it as a default plus an explicit exception list:

Shape language:
  Default: 0 (square). Cards, images, badges, tags, inputs, pagination — no radius.
  Exceptions:
    - dialogs / floating overlays: 6px
    - small meta badges: 4px
    - icon buttons and avatars: circles (50%)
  Never: pill chips (999px), radius > 8px (no token exists).

Three properties make this spec work. It states the default first — an unstated element inherits the page's shape language instead of receiving an invented radius. It enumerates exceptions as a closed list — anything not listed is square by definition. And it names the forbidden moves explicitly, because "we don't use pills" is exactly the kind of negative fact a reader cannot infer from a token table.

The default does not have to be 0. A friendly consumer product may declare "default 8px, pills for tag chips." The decision itself is the point: one default, one exception list, zero per-component improvisation.

Same Card, Two Shape Languages — the Tone Shift Is the Radius

Same content, same palette, same layout. The left card pairs square corners with a 1px hairline border; the right pairs 14px corners with a drop shadow. Neither is wrong — they are different brands. What is wrong is shipping both on the same site.

What Radius Communicates

RadiusRegister
0Technical, editorial, print, hardware, catalog
2–6pxQuiet softening — barely registers as "rounded"
8–16pxFriendly, consumer, app-like
16px+Soft consumer, playful, marketing-page — paired with restraint elsewhere it reads premium-consumer
999px (pill)Tag-cloud, filter-chip, strong casual statement
50% (circle)Icon buttons, avatars, markers — functional, not tonal

The jumps matter more than the values. Moving from 0 to 4px is nearly invisible; moving from 4px to a pill changes the register of the whole component. The largest jump on the axis — the pill — is also the most common violation.

Pills and Circles Are Statements

Fully-rounded shapes are the strongest move on the radius axis, the shape equivalent of a filled accent background (see the accent cost ladder). They are never a neutral default for badges, chips, or counters — yet generated output reaches for border-radius: 999px on nearly every small label, because pills dominate the training data's tag UIs.

One Badge, Three Statements — Square, Softened, Pill

Circles get a narrow functional carve-out — icon buttons, avatars, step markers — because a circle around an icon reads as "control," not as tone. That carve-out does not extend to text-bearing elements.

One Composition, One Radius

Within a single composition, sibling elements share one radius value. A 6px card holding 4px buttons next to a pill chip reads as three design systems fighting. Two rules keep nesting coherent:

  • Siblings match: all cards in a grid, all buttons in a row, all chips in a filter bar carry the same radius.

  • Nested corners subtract: when a rounded container holds a rounded child near its edge, the child's radius is the container's radius minus the gap between them — inner = outer − gap. Using the same radius on both makes the child's corner look over-rounded against the container's curve.

.dialog {
  border-radius: 12px;
  padding: 8px;
}
.dialog__media {
  /* 12px outer − 8px gap = 4px */
  border-radius: 4px;
}
Nested Radius — Same Value Looks Wrong, Subtracted Value Tracks the Curve

Shape Pairs With the Rest of the System

Shape language is not an isolated axis. Sharp corners belong with hairline borders, flat surfaces, and whitespace-driven depth; heavy rounding belongs with shadows, fills, and elevation. The pairings travel together because they encode the same tone:

If the shape language is…Depth comes from…Separation comes from…
Square-firstSurface-lightness steps1px hairlines, whitespace
RoundedShadows, elevationCard boundaries, fills

These pairings usually travel together because they encode the same tone. Deliberate counter-pairings exist — neo-brutalism runs hard offset shadows on square corners — and they work when declared site-wide as the shape language itself. What breaks the register is mixing per component, by accident.

Quick Reference

ScenarioTechnique
Starting a new project or design system docDeclare the radius default first, then a closed exception list
An element has no specified radiusIt inherits the default — never invent a per-component value
Project has no declared shape languageMeasure the product: grep border-radius / rounded- in production components and match; never introduce a radius the codebase does not already use
Badges, chips, countersFollow the default; pills only if the shape language declares them
Icon buttons, avatarsCircles are a functional exception in any shape language
Rounded child near a rounded container edgeinner = outer − gap
Reviewing generated outputSearch for any radius the spec did not ask for

Common AI Mistakes

  • Rounding by default. Training data over-represents rounded UIs, so unspecified corners come back rounded. A spec that never mentions radius has effectively requested 8px cards and pill chips.

  • Reading the radius token scale as an invitation. --radius-lg existing does not mean any component should use it — tokens are vocabulary, the declared default is grammar (the same trap as the accent budget).

  • Pill chips on everything small. Counters, badges, and meta labels get border-radius: 999px reflexively. Pills are a register statement, not a small-element default.

  • Mixing radii within one composition. A 6px card, 4px buttons, and pill chips in one view — each locally fine, jointly incoherent.

  • Equal nested radii. Applying the container's radius to a child sitting near its edge; the child must subtract the gap.

  • Pairing square with shadow, round with hairline. Shape, depth, and separation encode one tone together; swapping halves breaks the register.

When to Use

Declare a shape language

On every project, at the moment the design system or tone spec is first written. The declaration costs three lines (default, exceptions, never-list) and prevents the highest-frequency drift in generated UI code.

Apply the subtraction rule

Whenever rounded elements nest with a visible gap — dialogs with media, cards with thumbnails, buttons inside rounded bars.

Audit for unrequested radius

On any AI-generated or externally-contributed UI code for a project with a square or near-square default. Grep for border-radius and rounded- and compare every hit against the exception list — see Writing a Design Tone Spec an AI Agent Will Follow for the spec-side counterpart.

Revision History

CreatedUpdated