Segmented
One choice from a short list, with every option on screen at once.
import { Segmented, SegmentedItem } from "@dede-style/ui/segmented", then <Segmented value={v} onChange={setV} label="Text alignment"> wrapping a <SegmentedItem value="start" label="Start" /> per segment. It is controlled — the value lives with whoever owns the setting, and this renders it.
| Prop | Description | Default | Control |
|---|---|---|---|
| label | the group's accessible name: what the options are a choice of. Required | ||
| size | height: 32, 40 (default) or 48px, matching Button81012 | 10 |
|
| full | fill the container. Segments stay equal widths | false | |
| is | disables the whole control; the selected option stays visible | false |
Tokens
What a theme can move, where the props above are what an instance can move.
| Token | Description | Light | Dark |
|---|---|---|---|
| --segmented-thumb | the selected segment's fill. It is a token rather than a surface role because no role could serve both modes: on light the raised surface is pure white, so nothing can out-lighten it however many washes stack up beneath — on dark it sits a small step off the page, and a translucent track on a translucent stage climbed straight past it, leaving the selection reading as a hole. The dark value is picked by symmetry: it states the same selected-against-track relationship light does | --color-surface-raised | --neutral-35 |
| --segmented-radius | the track's corner, and the segments follow it — each one is inset by the track's padding, so its own radius is computed from this rather than declared. Resolves to --radius-4, the step Button's rounded shape, Card and TextInput all take, so a segmented control sitting beside any of them agrees unless a theme deliberately separates them | --radius-4 | --radius-4 |
A token appears here only when the value is not already reachable. Anything that resolves through a foundation token or a role needs none, because overriding that already moves it — and a second dial wired to the same value would let this one component drift from everything sharing the role. What is left is the values with no other lever at all.
These are public and overridable — there is no private custom property, because anything on :root is reachable by any stylesheet. What replaces privacy is a stability contract: primitives may move, roles are the guarantee, and these are the per-component escape from both.