FoundationsUpdated Oct 2, 2026
Rounding
Five steps plus two specials, control and pill, climbing slowly so a small control is proportionally less round than a card. Surfaces pick a step by role; controls never pick one at all, because Button, Input and Select take half their height.
Surfaces pick a step. Controls inherit theirs.
// Surfaces: pick the step by what the surface is<div className="rounded-xl corner-smooth" /> // a card or list item, 16<div className="rounded-2xl corner-smooth" /> // a panel, popover or menu, 20 // Controls: never set a radius. Button, Input and Select take their size's corner<Button size="lg">Save</Button> // 32 tall, --radius-size-lg = 12 // Nest concentrically: child radius = parent radius − the gap between them<div className="rounded-2xl p-1"> // 20 <div className="rounded-xl" /> // 20 − 4 = 16</div> // Pills and dots<span className="rounded-full corner-sharp" />- Tailwind names map by role.
rounded-lgis the scale'ssm, 12px, because that is the job it already did here. Read the table below rather than Tailwind's docs. rounded-sm,roundedandrounded-mdare one step. All three resolve to--radius-control, 8px. Writerounded-mdin new code.--radius-2xlhas no Tailwind name. Nothing maps onto it, so a hero surface at 48px writesrounded-[var(--radius-2xl)]. That names the token rather than a raw value, so it is not the case flagged below.- An arbitrary radius is a review flag.
rounded-[10px]is either a control size, which the primitive already handles, or a step the scale is missing.
Every surface step as it computes in @highlight/ui.
--radius-control8px--radius-sm12px--radius-md16px--radius-lg20px--radius-xl24px--radius-2xl48px| Utility | Token | Value | Where it goes |
|---|---|---|---|
rounded-md | --radius-control | 8px | Checkboxes and small toggles |
rounded-lg | --radius-sm | 12px | Inputs, buttons, tags |
rounded-xl | --radius-md | 16px | Cards, list items |
rounded-2xl | --radius-lg | 20px | Panels, popovers, menus |
rounded-3xl | --radius-xl | 24px | Large controls, sheets |
| None | --radius-2xl | 48px | Hero surfaces |
rounded-full | --radius-pill | 999px | Pills, dots, avatars |
A corner that grows slowly with the height, 8 at 20 up to 12 at 32, so a field and a button of one size share a corner.
--radius-size-xs20px tall · 8pxown value--radius-size-sm24px tall · 9pxown value--radius-size-md28px tall · 10pxown value--radius-size-lg32px tall · 12px= --radius-sm--radius-size-xl40px tall · 20px= --radius-lg--radius-size-2xl48px tall · 24px= --radius-xlA control's corner grows more slowly than its height, so a button reads as a softened rectangle beside the fields and cards around it rather than as a pill: 8 at 20, 9 at 24, 10 at 28, then --radius-size-lg meets the surface scale at --radius-sm (12), and xl and 2xl are lg and xl. xs, sm and md carry their own values and stay off the surface scale so a card can never pick them. Despite the shared names, --radius-size-sm is a control size and --radius-sm is a scale step.
A child's radius is the parent's minus the gap. Anything else pinches at the corner.
rounded-2xl · p-1 · rounded-xl20 − 4 = 16. Concentric.rounded-2xl · p-1 · rounded-2xlEqual radii. The gap pinches at the corner.Three utilities, and the choice between them is not taste.
.corner-smooth { -electron-corner-smoothing: system-ui; corner-shape: squircle; }.corner-squircle { corner-shape: squircle; } /* half-height corners: no straight edge to ease into */.corner-control { corner-shape: var(--corner-control); } /* Button, fields and list rows: superellipse(1.5) */.corner-sharp { -electron-corner-smoothing: 0%; corner-shape: round; } /* rounded-full */corner-smoothon every surface. Electron draws its continuous curve; browsers that supportcorner-shapedraw a squircle.corner-squircleon half-height corners. Electron's smoothing eases into a straight run of edge, and a half-height corner has none, so it reads as a pill.corner-controlon controls. A superellipse a little softer than the squircle, for a corner well under half the height. Button, the field shell and list rows already carry it.corner-sharpwithrounded-full. A squircle on a clamped radius replaces the round ends rather than softening them.
/* packages/ui/src/styles/tokens.shape.css */:root { --radius-sm: 12px; /* inputs, buttons, tags */ --radius-md: 16px; /* cards, list items */ --radius-lg: 20px; /* panels, popovers, menus */ --radius-xl: 24px; /* large controls, sheets */ --radius-2xl: 48px; /* hero surfaces */ --radius-control: 8px; /* checkboxes and small toggles */ --radius-pill: 999px; /* pills, dots, avatars */} /* packages/ui/tailwind.config.ts maps Tailwind's names onto the scale by role, not by name */borderRadius: { sm: 'var(--radius-control)', DEFAULT: 'var(--radius-control)', md: 'var(--radius-control)', lg: 'var(--radius-sm)', xl: 'var(--radius-md)', '2xl': 'var(--radius-lg)', '3xl': 'var(--radius-xl)', full: 'var(--radius-pill)',}