opsinjs
FoundationsToken familiesShape

Shape

Why the corners in a health product are superellipses rather than circular arcs, what shape is allowed to mean, and the one thing it must never carry.

Overview

Shape is the smallest foundation and the one with the largest gap between how much it matters and how much thought it usually gets. It comes down to two decisions: how big the corners are, and what curve they are made of.

The second is the interesting one. A CSS border-radius draws a circular arc, and a circular arc meets a straight edge with a discontinuity: the curvature jumps from zero to its full value at a single point. At a 4px radius nobody notices. At the 16 to 28px radii a generous, iOS-idiom health product actually uses, the eye reads that discontinuity as a pinch. The corner looks tighter than it is, and a row of cards looks slightly wrong in a way most people cannot name.

A squircle is a superellipse, and it distributes the curvature continuously into the straight edge instead. It is the shape Apple's platform UI has used for years, and until recently the web could not draw it without an SVG mask or a clip path. The CSS corner-shape property changes that, and opsinjs uses it as pure progressive enhancement: browsers that have it get squircles, browsers that do not get an ordinary rounded rectangle at the same radius. The difference is taste, never function.

Shape is often confused with elevation. A rounder card is not a raised card. Depth is material and layer; shape is geometry.

How it works

Three pages sit under this one: the radius scale, which is a ladder derived from a single value plus the rule for nesting one radius inside another; Squircle, which is the corner-shape mechanism and its degradation; and the generated tokens.

Two rules run across all three.

1. Radius is a ladder, not a set of choices. Every radius in the system is derived from one root value by a fixed multiplier, so changing the character of the product from square to generous is one number rather than a search. The rungs are named for the size of thing they belong to rather than for their value. Those things are a control, a card and a sheet.

2. Shape never carries clinical meaning. A rounder card is not a friendlier result. A sharper corner is not a more serious one. Radius may express what kind of object something is, and it may express nothing at all about the value inside. A pill-shaped chip reads as a label, and a large-radius sheet reads as a surface that came from the bottom edge. Status is colour, icon and word, always and only: see Principles.

There is a corollary worth stating separately, because it is the one people break: do not use a full radius to mean "small". A fully rounded pill and a large-radius card are different objects, not different sizes of the same one.

Using it

Match the rung to the object, then stop. Controls take the small rungs, cards the middle, sheets and modal surfaces the large. If you are choosing between two adjacent rungs, the difference does not matter and the smaller one is usually right.

Nest concentrically. An inner radius equals the outer radius minus the padding between them. Get this wrong in either direction and the gap between the two curves visibly varies around the corner. The full rule and the failure cases are on Radius scale.

Do

A result card at the card rung, containing a range bar inset by one space step, with the bar's radius reduced by that step. The two curves stay parallel all the way round the corner.

Don’t

The same card with the inner element at the same radius as its parent. At the corner the gap between them pinches to nothing on the diagonal, which reads as a rendering fault rather than a design choice.

Do not square a corner for emphasis. Emphasis on a health screen is a status question. A squared-off urgent card is a second escalation channel that nobody can read and that disappears in a screenshot.

Do not animate a radius. It is expensive to composite, it draws attention to the container rather than the content, and it is one of the more reliable ways to make a transition look broken part-way through.

Do not reach for the squircle on every rounded box. The corner shape is not applied globally and no component stamps an attribute for it. Each surface that wants it opts in by carrying an inline corner-shape property that reads var(--opsin-corner-shape) beside its radius, which is how Card, Callout and Dialog draw it. Keeping it to the surfaces the system draws leaves third-party components, embedded content and anything with an incidental radius alone, and a browser without support simply keeps the ordinary rounded rectangle.

Tokens

The root radius, the ladder multipliers, the named rungs and the corner shape itself are generated from tokens/shape.json by scripts/build-tokens.mts. The full table with a rendered specimen per rung is on Shape tokens.

TokenWhat it controlsUsed by
--opsin-radius-base0.875remThe one radius the whole ladder is derived from.accordion, alert-banner, button, callout, card, care-card, combobox, empty-state, field, menu, metric-tile, popover, questionnaire, radio-group, range-bar, reading-input, result-card, scale-input, segmented-control, select, skeleton, symptom-picker, tab-bar, textarea, timeline-entry, toast, tooltip, trend-sparkline
--opsin-radius-none0remFull-bleed media and anything that meets a screen edge.no component
--opsin-radius-xs0.25remThe floor for a nested corner. Tags inside a chip, a swatch inside a legend.popover, range-legend, surface, symptom-picker, term, tooltip
--opsin-radius-sm0.4375remInputs and small controls inside a card.card, checkbox, combobox, consent-sheet, dialog, divider, field, log-sheet, menu, number-field, progress, reading-input, scroll-area, select, sheet, skeleton, slider, switch, tab-bar, tabs, toast, visually-hidden
--opsin-radius-md0.875remThe default. Buttons, chips, cards.alert-banner, button, callout, card, combobox, empty-state, menu, metric-tile, popover, questionnaire, radio-group, result-card, scale-input, segmented-control, select, skeleton, symptom-picker, tab-bar, textarea, toast, tooltip
--opsin-radius-lg1.3125remCards on a phone, where the card is nearly the width of the screen.accordion, card, combobox, disclaimer-note, menu, popover, result-card, scroll-area, segmented-control, select, surface, toast
--opsin-radius-xl1.75remSheets and dialogs. Applied to the leading edge only when the surface meets a screen edge on the other side.alert-banner, dialog, sheet
--opsin-radius-inner-cardmax(calc(0.875rem - 5 * var(--spacing)), var(--opsin-radius-xs))A box inside a card at the comfortable density (card padding p-5).no component
--opsin-radius-inner-card-compactmax(calc(0.875rem - 4 * var(--spacing)), var(--opsin-radius-xs))A box inside a card at the compact density (card padding p-4).no component
--opsin-corner-shapesuperellipse(4)The squircle curvature. Degrades to `round` where corner-shape is unsupported.alert-banner, callout, card, dialog, metric-tile, result-card, sheet
--opsin-border-hairline1pxEvery boundary in the system by default.button, surface
--opsin-border-emphasis2pxThe boundary of a surface carrying `attention` or `urgent`, where the boundary is one of the three non-colour carriers of the status.disclaimer-note, field, surface
--opsin-border-focus2pxThe focus ring. Always 2px with a 2px offset, always in a colour measured against both the surface and the page behind it, and never removed. See /docs/accessibility/keyboard-and-focus.accordion, body-map, button, card, checkbox, combobox, consent-sheet, link, menu, number-field, popover, questionnaire, radio-group, scale-input, scroll-area, segmented-control, select, slider, switch, symptom-picker, tab-bar, table, tabs, term, textarea, toast, tooltip
--opsin-border-focus-offset2pxOffset for the focus boundary.accordion, body-map, button, card, checkbox, combobox, consent-sheet, link, menu, number-field, popover, questionnaire, radio-group, scale-input, scroll-area, segmented-control, select, slider, switch, symptom-picker, tab-bar, table, tabs, term, textarea, toast, tooltip

Accessibility impact

Shape has less accessibility surface than any other foundation, and exactly three things on it matter.

  • A radius can eat a touch target. A heavily rounded control has less clickable area at its corners than its bounding box suggests. The 44pt floor from Density and touch is measured on the interactive area, not on the visual box, and a large radius on a small control is the most common way that measurement quietly fails.
  • A radius can eat a focus ring. The focus outline follows the border radius, and where a control is clipped by an ancestor with a smaller radius the ring is clipped with it. This is why the focus indicator is drawn outside the box with an offset. The rule is on Interaction states.
  • Shape must never be an information channel, because it survives no assistive technology at all. There is no way to announce a corner. Anything a radius is carrying is information a screen-reader user does not have.

Radius does not affect contrast, so nothing on this page is measured; the corner-shape support matrix is published by <BrowserSupport> on Squircle.

  • Radius scale has the ladder, the rung names and the nesting rule.
  • Squircle covers corner-shape, the superellipse parameter and the documented degradation.
  • Materials is the foundation that shape is most often confused with, and the one that actually carries depth.

On this page