Skip to content
Monogem

Radius

Overview

Radius sets how round the system's corners are. Every value comes from the master numeric scale, but Radius ships one semantic knob — --radius — plus four sizes derived from it. Turn the knob and the whole UI rounds together.

Why a token here when Spacing shipped none? Because shadcn components read --radius directly (rounded-lg → var(--radius)). The token isn't indirection — it's the interface the components expect, and the single control that keeps every corner in step.

Source of truth: tokens/semantic.css in the Monogem codebase.

Principles

  • One knob rounds the system. Every size is derived from --radius; change that one value and sm/md/lg/xl all move with it.
  • Sourced from the scale. The base is --scale-2 (8px), so at the default every size lands on a scale step — nothing drifts off the shared ruler.
  • Size-named, intent in the recipe. Tokens are named by size (like the shadow ramp); the "use for" column carries the meaning (--radius-md = button roundness).
  • full is the one exception. A single off-scale value (9999px) for pills and avatars.

Tokens

TokenFormulapx (default)Scale stepUse for
--radius-sm--radius − 4px4--scale-1Tags, inputs
--radius-md--radius − 2px6--scale-1-5Buttons
--radius-lg--radius8--scale-2Cards, popovers (default)
--radius-xl--radius + 4px12--scale-3Modals, large surfaces
--radius-full9999px—off-scalePills, avatars, badges, circular

--radius itself is the base knob: --radius: var(--scale-2) (8px). In Tailwind these map to rounded-sm / rounded-md / rounded-lg / rounded-xl / rounded-full — tokens and utilities never disagree.

The single knob. Every size is calc()-derived from --radius. Re-point --radius once — in :root or inside a media query — and the whole set scales together. No per-size edits.

Rationale

Why ship a token when Spacing didn't? Spacing shipped zero because Tailwind already maps p-4/gap-4 onto scale steps, so a --space-md alias would be pure indirection. Radius is the reverse: shadcn/ui components consume --radius directly, so the token is the real interface — and it adds a single knob spacing never needed.

Why an 8px base (not shadcn's 10px)? With the base at 8px, the standard shadcn derivations land on exact scale steps — 4 / 6 / 8 / 12. A 10px base would push two sizes (10, 14) off our scale. 8px keeps the whole set on the shared ruler.

Why calc() instead of pointing each size at a scale token? The calc() form is what makes --radius a real knob — change it and every size follows, exactly as shadcn components expect. Pointing each size at its own scale token would read cleanly but break that single control.

Why is full 9999px, not 50%? 50% turns a rectangle into an ellipse; a large fixed radius clamps to a clean pill on any shape and a circle on squares.

What about responsive corners? Radius rarely changes across breakpoints, so it ships no breakpoint tokens (that's Grids / Layout's job). If a real need appears, the single knob covers it in one line: @media (min-width: 1024px){ :root{ --radius: var(--scale-3); } }.

Do / Don’t

Do

  • Use a --radius-* token for every corner; pick the size by task from the table.
  • Match the size to the component: inputs sm, buttons md, cards lg, modals xl, pills full.
  • Re-point --radius (once) to re-round the whole system — don't edit sizes individually.

Don’t

  • Hard-code corner values (6px, 10px) — reach for a token.
  • Add a --radius-none — a square corner is --scale-0 / rounded-none.
  • Put --radius-full on a rectangle expecting a circle — it makes a pill; circles need a square.