Skip to main content

Learning Track

Filter content by your role to see the most relevant sections

Radius & Shape Foundations

Give your interface a consistent visual personality: a stepped radius scale, semantic shape roles for surfaces and controls, the pill-vs-regular contract, and how shape interacts with density, focus, and brand identity.

Darian Rosebrook
Staff Design Technologist, Design Systems Architect
Expertise:Design Systems, Visual Design, Tokens

Why This Matters

Shape is the foundation users perceive fastest and describe worst. Nobody articulates "the corner radii are inconsistent"; they say the product feels off, or cheap, or like two teams built it. A 4px input inside a 12px card on an 8px modal is exactly that—three shape languages in one screen, each defensible, together incoherent.

Radius is also brand infrastructure. Sharp corners read technical and serious; generous curves read friendly and consumer. When shape lives in tokens, that personality dial is a mapping change—swap the semantic roles onto different scale steps and the whole product moves together. When shape lives in per-component CSS, rebranding means archaeology.

This page covers the radius system as built here: the shape.radius scale in the core tokens, the semantic shape roles above it, and the control-level pill contract.

Core Concepts: The Scale, the Roles, the Contracts

An Eight-Step Scale with a Named Middle

The core scale in ui/designTokens/core/shape.tokens.json:

// shape.radius (values)
none: 0px     01: 2px     02: 4px     medium: 6px
03: 8px       04: 16px    05: 32px    full: 9999px

The climb is roughly doubling with intent: hairline (2) for chips and tags, control (4) for inputs and buttons, the named medium (6) as the workhorse control corner, up through 8 for cards and 16 for large surfaces, with full as the pill. Note that medium sits between 02 and 03 numerically—a named token inserted where the scale had a gap. It is a wart worth knowing: the scale's numbering is historical, the semantic names are the interface.

Semantic Roles Are the Interface

Consumers never touch the numbered steps. The semantic layer maps them to roles:

// ui/designTokens/semantic/shape.tokens.json (excerpt)
"shape": {
  "radius": {
    "extraSmall": "{shape.radius.01}",   /* 2px  */
    "small":      "{shape.radius.02}",   /* 4px  */
    "default":    "{shape.radius.02}",   /* 4px  */
    "medium":     "{shape.radius.03}",   /* 8px  */
    "large":      "{shape.radius.04}",   /* 16px */
    "extraLarge": "{shape.radius.05}"    /* 32px */
  },
  "control": {
    "radius": {
      "default": "{shape.radius.medium}",  /* 6px */
      "pill":    "{shape.radius.full}"     /* 9999px */
    }
  }
}

Read the two mappings as two different questions. Surface roles (small…extraLarge) answer "how rounded is this container?" and scale with the container—a badge takes extraSmall, a card takes medium or large. Control roles answer "how rounded is this interactive element?" and hold constant across sizes: every button is control.radius.default (6px) regardless of its width, which is why a row of differently-sized controls still reads as one family.

The Pill Contract

full: 9999px exists for one shape: the pill, where corner radius equals half the height. Expressed as a huge fixed value, it always equals half of any height up to ~20000px—the browser clamps. The contract is that pills are a deliberate variant (control.radius.pill) for filter chips, toggle tracks, and avatar frames—not a slider you tune. A 12px-radius button that "looks sort of pill" is drift; it is either 6 or it is a pill.

Shape Interacts With Everything

  • Focus rings: the focus outline follows the element's radius—the semantic control.border.focusWidth (the 2px thick width) pairs with the same corner value, so focused pills get pill-shaped rings for free.
  • Elevation: shadows take the element's radius; a 16px card with an 8px shadow curve reads as mismatched weight.
  • Density: tight-density layouts keep the same radii—shape is personality, not space, and rescaling it with density muddies both systems.
  • Nesting: inner corners step down one role from their container (a 16px modal hosts 8px cards hosts 6px controls). Equal or ascending inner radii look like alignment errors.

System Roles: Where Shape Decisions Land

Design Impact

Designers own the personality dial—which semantic roles map to which steps—and the nesting ladder. The review question is "which role is this corner?" with the same discipline as color and spacing: a radius that cannot name its role is a radius the system cannot change.

Engineering Impact

Engineers own consumption hygiene: scoped component tokens resolving semantic roles (the Card contract ships --ds-card-size-radius-medium resolving a semantic shape value with a literal fallback), and the rule that radii never appear as literals in rules.

Brand Impact

Brand owns the extremes. A rebrand toward sharpness moves the semantic mapping down-scale in one file; toward softness, up. That is the entire cost of a shape rebrand when shape is tokenized—and a cross-codebase audit when it is not.

Design & Code Interplay

In the design tool, shape tokens appear as corner-radius variables with the same role names, applied through component styles rather than per-frame tweaking. The comp-side contract that matters most: nested containers visibly step their radii down, and pills are drawn as true pills—not as approximately-round rectangles the implementer must guess about.

Because radius is brand infrastructure, the design library keeps one shape mode per brand—same structure as the color brand layers—so previewing a softer brand is a mode switch, not a re-draw.

Applied Example: A Filter Bar, Shaped Coherently

Ship a shape-heavy pattern: a filter bar with pill chips inside a card, above a data table.

  1. Assign by role, top-down: the card is a surface → radius.medium semantic (8px). The chips inside are controls and pills → control.radius.pill. The table below is sharp-cornered data → radius.none with hairline borders from the border system.
  2. Check the ladder: card 8 → chips pill (the exception that reads correctly because pills are absolute), and no nested element equals its container's radius.
  3. Interactions keep their shape: chip removal animates opacity and translate tokens, never the corner—a radius that animates reads as morphing, and morphing is motion-system vocabulary, not a filter chip's job.
  4. Focus shapes follow: tabbing through the chips shows pill-shaped focus rings (2px focusWidth) because the outline inherits the radius.
  5. Brand check: switching to a softer brand moves the semantic mapping; the bar's relationships survive because everything consumed roles.

Five checks, zero new numbers. The bar looks like the rest of the product because it is the rest of the product, shape-wise.

Constraints & Trade-offs

  • Fixed steps vs optical radius: huge surfaces technically want proportionally larger radii to look equal; a stepped scale accepts slight optical variance for predictability. Where it matters (hero cards), pick the next role—do not compute per-element.
  • One control radius vs per-variant: holding 6px for all controls costs expressiveness (no extra-round secondary buttons) and buys the strongest version of "controls are one family."
  • 9999px trick vs explicit 50%: the huge-value pill cannot express elliptical or asymmetric corners; those rare cases fall to dedicated components, not the scale.
  • Shape vs density decoupling: keeping radii constant across density modes preserves identity but means dense layouts carry rounded corners into tight spaces—the accepted cost, revisited only if clipping artifacts appear.

Common Pitfalls & Failure Modes

1. Literal radii

/* Bad */
.input { border-radius: 4px; }
/* Good */
.input { border-radius: var(--semantic-control-radius-default, 6px); }

2. Nesting without stepping

/* Bad: Card 8px hosting an 8px panel */
.panel { border-radius: var(--semantic-shape-radius-medium); }
/* Good: Step down inside */
.panel { border-radius: var(--semantic-shape-radius-small); }

3. Almost-pills

/* Bad: Hand-tuned fake pill */
.tag { border-radius: 14px; }
/* Good: Commit or don't */
.tag { border-radius: var(--semantic-control-radius-pill); }

4. Scaling radius with size

Corner radius that grows with an element's width (5% of width, say) destroys the constant-control contract and makes sibling controls disagree. Shape is a role, not a ratio.

Shape System Health Metrics

Signal 1: Role coverage

  • Healthy: every border-radius in product CSS resolves through a semantic role (or scoped contract); the numbered steps appear only inside the token sources.
  • Warning: a handful of literal radii survive from a pre-token era—each an orphan the next rebrand cannot reach.
  • Critical: literals are the norm; a shape rebrand would be a find-and-replace across the codebase, which is the failure the layering exists to prevent.

Signal 2: Ladder integrity

  • Healthy: nested corners step down one role from their container everywhere; a spot check of any modal shows panel → card → control in descending order.
  • Warning: equal or ascending inner corners appear on one surface—reading as an alignment error users cannot name.
  • Critical: no ladder survives inspection; nesting looks accidental product-wide.

Signal 3: Control constancy

  • Healthy: all controls resolve control.radius.default (6px) or the deliberate pill, at every size.
  • Warning: one control family picked its own corner—sibling controls now disagree subtly on every screen they share.
  • Critical: per-variant radii are common; the "one family" promise is gone.

Migration Strategy: Collecting the Corners

  1. Inventory the literals: every border-radius value with its surface. Dedupe; most products discover five or six distinct numbers serving three roles.
  2. Assign roles top-down: each surface gets its semantic role (container scale with size, controls constant, pills deliberate); literals snap to the role's step with nearest-step tolerance.
  3. Fix the ladder violations: nesting that equalled or ascended gets its step-down; the visual change is small and the "designed" reading is immediate.
  4. Sweep and lint: the border-radius literal pattern joins the per-directory lint as areas clean, keeping the rebrand cost at one mapping file.

Real-World Case Studies

Case 1: The rebrand that took an afternoon

A sharper brand direction moved three semantic mappings down-scale in one file, rebuilt, and shipped—every surface, control, and pill moved together because nothing consumed a literal. The team that had resisted the layering became its advocates at the sight of a rebrand reviewed as a generated diff.

Case 2: The almost-pill that fooled everyone

A 28px-tall chip with border-radius: 14px looked like a pill until focus arrived—the ring inherited a true-radius sibling and the mismatch exposed the fake. Committing to control.radius.pill fixed shape and ring in one token.

Case 3: The card inside the card

A media card hosted an inner panel at the same 8px corner; users read the inner edge as a rendering glitch. The step-down to small (4px) made the nesting read as structure—the ladder rule earning its keep on the least glamorous surface in the product.

Verification Checklist

Additional Resources

  • Borders & Strokes — the lines that pair with these corners (/blueprints/foundations/borders)
  • Elevation & Shadows — shadows follow shape (/blueprints/foundations/elevation)
  • The sources — ui/designTokens/core/shape.tokens.json, ui/designTokens/semantic/shape.tokens.json

Related Concepts

Reflection Questions