Skip to content
ArcadiaDesign

Scale

Everything you press is a pill.

Each step's corner at three times its size.
TokenUseCornerValue
Bars and tiny marks.Round
Key caps.Round
Small surfaces and inline code.Round
Nested wells and tooltips.Round
Rows and fields: the sidebar, palette results, inputs.Round
Code windows, menus and settings groups.Continuous
A palette and a user's message.Continuous
Large display surfaces.Continuous
Everything you press: buttons, icon buttons, segmented controls, tabs, badges, chips, the compact composer.Round

Inner radius = outer radius − inset. Menus, card rows and trays derive their corners from their frame and padding. A composer stays round around its round 24px send button: 12px + 8px padding + a 1px border gives --radius-frame-composer. Changing the inset changes the inner corner with it.

Surfaces follow nesting: a surface's radius is its child's plus the padding between them, so corners stay concentric. The large steps take continuous corners where the browser has them.

Concentric corners

Outer = inner + padding. A part within reach of its frame's corner takes the frame's radius less its inset, so the gap between the two curves stays even all the way round.

20 = 14 + 6Tray: an option
15 = 9 + 6Menu: an item
15 = 8 + 7Card: a row, behind the 1px frame
31 = 14 + 17Dialog: a 28px pill, behind the 1px frame
24 = 12 + 12Popover: a 24px pill
15 ≠ 15 + 6Wrong: equal radii nested, the gap pinches at the corner
Each pair's top-left corner at twice its size: the frame's radius, the part's, and the inset between them, measured from the frame's outer edge (its border included).
  • A child within reach of a corner (inset d below the frame's radius R on both axes) has r = max(4, R − d). Never equal radii on nested parts unless the part is flush and clipped.
  • The inset at a reached corner is the same on both axes. If it isn't, fix the padding, not the radius.
  • A pill fixes its own radius (h / 2), so a frame that hosts one in its corner derives its radius from it: R = h / 2 + d. Those are the frame radii below, the same in both corner modes and round: a pill's round cap is never concentric with a continuous corner.
  • Components derive it for you: a container sets the nest pair (`stylex.env.nest.frame`), its parts read `stylex.env.nest.radius`, and the lint (`nest-derived`, `frame-radius`) refuses a radius worked out by hand.
  • A part shares its frame's corner shape through any wrapper between them: `cornerShape: nest.corner`, the frame naming a continuous corner in `nest.frame(R, pad, corner)` or `nest.shape(corner)`. A round part in a continuous frame flares at the corner's middle by an eighth of the frame's radius, 2px in a 15px menu; the lint (`nest-corner`) refuses `inherit`, which reads the parent only.

Clear of the curve

What can't be concentric keeps clear: a line of text, or a row too tall for the radius it would need, sits on the corner's centre line on one axis, its middle R from that edge.

Pinched: 15 from both edges, 11 from the curve
Clear: its line centred 31 down, 14 from the curve, as the settings window's title sits
A 13px title in a dialog's 31px corner, twice its size: at the 15px inset it comes within 11 of the curve; centred on the corner's line it keeps the 14 its side does.
  • The inset is `nest.capInset(R, h)` from the frame's edge (a dialog's title line, a popover's first and last lines, the settings window's nav title and Docs row), or `nest.cornerLine(h)` past a nest frame's pad.
  • A capsule keeps its own shape: where it can't be concentric it takes extra margin, never a radius of its own.
  • The layout check's `corner` rule reads every shot at each corner: the gap to the curve never pinches more than 1.5px under the part's inset from the edges (2px for a text, by its cap band), and a part that draws its own corner never flares more than 1.5px past it.

So a frame's padding near its corners comes from its radius.

Frame radii

Derived from what sits in their corner, so the corner and its control are one curve.

TokenWhat sits in the cornerValue
Composer: the 24px send pill, 8 in behind a 1px frame (12 + 8 + 1).
Tray: a 24px pill in the footer corner at 8 (12 + 8).
Popover: a 24px pill at the 12px pad (12 + 12).
A list popover: a 24px row at the 6px pad (12 + 6).
Dialog: a 28px pill at the 16px pad, behind a 1px frame (14 + 16 + 1).
Card: a 6px rounded control 8 in, behind a 1px frame (6 + 8 + 1).

Scrollers

A scroller's thumb keeps out of its corners too: 4px wide, 2px from its side, its straight run ending r − 2 from the outer edge, so its cap is concentric with the corner.

Line 1 of a list that scrolls
Line 2 of a list that scrolls
Line 3 of a list that scrolls
Line 4 of a list that scrolls
Line 5 of a list that scrolls
Line 6 of a list that scrolls
Line 7 of a list that scrolls
Line 8 of a list that scrolls
Line 9 of a list that scrolls
Line 10 of a list that scrolls
Line 11 of a list that scrolls
Line 12 of a list that scrolls
Line 13 of a list that scrolls
Line 14 of a list that scrolls
Hover to show the thumb: it stops short of the 20px corners.

A scroller names its corner with --scroll-radius and opts in with data-scroll-inset.

Continuous corners

Large surfaces set corner-shape: var(--corner).

This browser has no corner-shape, so both lines draw round. Open the page in Chromium 139 or later to see the difference.

Continuous corners read smaller than round ones at the same radius, so where they apply the large steps go up a quarter: --radius-xl 12 to 15, --radius-2xl 16 to 20 and --radius-4xl 24 to 30. Elsewhere --corner is round and the steps stay 12, 16 and 24.

Where they apply

  • Continuous: stream cards, menus, hover cards, toasts and a product's user bubble. Inset rows inherit their frame's shape.
  • Round: pills (--radius-full), the frames that host a pill in their corner (trays, popovers, dialogs, a composer), standalone rows and fields, tooltips and nested cards.
Files changed
src/routes/settings.tsx
A stream card is continuous; the nested card inside stays round.
CSS

Where the browser has it (Chromium 139 and later) that is superellipse(1.6): the curve eases in from the straight edge instead of starting abruptly, the way a machined part does.