Components
Every primitive in components/ui, drawn from one size contract in components/ui/sizing.ts. Hover, active and focus are forced here through a forceState prop that exists only for this page — app code never sets it.
The inventory
Fourteen components. The props are the Figma variant properties, one for one, and the file is the description each Figma component carries.
| Component | Props | States | File |
|---|---|---|---|
| Button |
| default · hover · active · focus-visible · disabled · loading | components/ui/button.tsx |
| IconButton |
| default · hover · active · focus-visible · disabled | components/ui/icon-button.tsx |
| Input |
| default · hover · focus-visible · disabled · invalid | components/ui/input.tsx |
| Select |
| default · hover · focus-visible · disabled | components/ui/select.tsx |
| Chip |
| static | components/ui/chip.tsx |
| TimePill |
| static | components/relay/time-pill.tsx |
| TierIcon |
| static | components/relay/tier-icon.tsx |
| Kbd | static | components/ui/kbd.tsx | |
| Panel |
| static | components/ui/panel.tsx |
| SectionBand |
| static | components/ui/section-band.tsx |
| Toast |
| static · entering · leaving | components/ui/toast.tsx |
| Skeleton | static — no shimmer | components/ui/skeleton.tsx | |
| EmptyState |
| static | components/ui/empty-state.tsx |
| ErrorState |
| static | components/ui/error-state.tsx |
The size contract
Four heights, and height decides everything else: radius is a function of height (r = round(h × 0.28)), as is the icon size and the horizontal padding. Anything sharing a row shares one entry — no 32 beside 40, ever.
| Step | Height | Radius | Where |
|---|---|---|---|
| xs | 28 | 8 | Chips, time pills, controls inside a row's meta cluster |
| sm | 32 | 8 | Secondary actions inside dense surfaces |
| md | 40 | 10 | The default: inputs, selects, most buttons |
| lg | 48 | 12 | The primary action |
…
…
…
Button
The primary button is filled with --text-1, not the accent. The accent is a focus ring and a link, and nothing else — a saturated fill inside a tinted card makes the button louder than the tier it sits in, which is the one thing this product’s ranking cannot survive.
| Button variant | Default | Hover | Active | Focus visible | Disabled | Loading |
|---|---|---|---|---|---|---|
| Primary | ||||||
| Secondary | ||||||
| Ghost |
Icon button
| Icon button variant | Default | Hover | Active | Focus visible | Disabled |
|---|---|---|---|---|---|
| Ghost | |||||
| Secondary |
Input and select
| Input | Default | Hover | Focus visible | Disabled | Error |
|---|---|---|---|---|---|
| Text |
| Select | Default | Hover | Focus visible | Disabled |
|---|---|---|---|---|
| Carrier |
Chip, time pill, tier icon
A chip carries a tier’s text and border, never a saturated fill. Shape carries the tier too — filled octagon, filled triangle, hollow circle, hollow square — so the ranking survives having the hue taken out.
Surfaces
A card is one hairline plus one shadow step. In dark, alpha-white borders compound, so a bordered card never contains another — the inner box steps the surface instead.
Lithium battery orders missing hazmat labels
Feedback and absence
Nothing waiting on you
When something needs a decision, it shows up here first.
Tier icon and time pill
Shape carries the tier before colour does: octagon, triangle, circle, square. The pill never chooses its own colour from lateness. Two hours late in Up next is Up next amber.
| Tier icon | Act now | Up next | When you can | For your info |
|---|---|---|---|---|
| Plain | ||||
| Safety |
| Time pill | Act now | Up next | When you can |
|---|---|---|---|
| Late, minutes | |||
| Late, hours | |||
| Due in | |||
| Due at | |||
| Tomorrow |
Anatomy
What each part is made of, read off the running component. A number here is a number the browser reports, not one typed into a document.
- 1
- 2
- 3
- 4
- 1
- 2
- 3
- 4
- 1
- 2
- 3
- 1
- 2
- 3
- 4
The same components in wire
Not a filter, and not a second set of components: the chroma multiplier goes to zero, radii collapse, depth turns off, and the sans becomes the mono. Everything below is the same code as above.