SegmentedControl
A radio-group style control where one segment is selected at a time, with outline, filled, and pill variants, horizontal and vertical orientations, and full keyboard navigation
Installation
pnpm add @tessinaui/uiUsage
import { SegmentedControl, SegmentedControlItem } from "@tessinaui/ui";<SegmentedControl defaultValue="week">
<SegmentedControlItem value="day" label="Day" />
<SegmentedControlItem value="week" label="Week" />
<SegmentedControlItem value="month" label="Month" />
</SegmentedControl>Playground
Configure every property of the SegmentedControl component interactively:
Showcase
All variants, sizes, orientations, icon combinations, and states:
API Reference
SegmentedControl Props
| Prop | Type | Default | Description |
|---|---|---|---|
value | string | — | Controlled selected value |
defaultValue | string | — | Initial selected value for uncontrolled usage |
onValueChange | (value: string) => void | — | Callback fired when selection changes |
variant | "outline" | "filled" | "pill" | "outline" | Visual style |
size | "xs" | "sm" | "md" | "lg" | "md" | Size applied to all items |
orientation | "horizontal" | "vertical" | "horizontal" | Layout direction |
disabled | boolean | false | Disables all items in the group |
dir | "ltr" | "rtl" | "ltr" | Text direction — RTL reverses layout |
SegmentedControlItem Props
| Prop | Type | Default | Description |
|---|---|---|---|
value | string | required | Unique value identifying this item |
label | string | — | Visible label text |
leadingIcon | React.ReactNode | — | Icon rendered before the label |
disabled | boolean | false | Disables this item individually |
aria-label | string | — | Accessible label (required when no visible label is provided) |
Variants
| Variant | Unselected | Selected |
|---|---|---|
outline | Border + transparent background | White background, no border, subtle shadow |
filled | Transparent, muted text | Primary brand fill + on-primary text |
pill | Transparent, default text | Primary brand fill + on-primary text |
Sizes
| Size | Wrapper height | Use case |
|---|---|---|
xs | 40px | Compact toolbars |
sm | 48px | Secondary controls |
md | 52px | Default, most use cases |
lg | 52px | Prominent controls with larger text |
Controlled vs Uncontrolled
Controlled — manage selection state externally:
const [view, setView] = useState("week");
<SegmentedControl value={view} onValueChange={setView}>
<SegmentedControlItem value="day" label="Day" />
<SegmentedControlItem value="week" label="Week" />
<SegmentedControlItem value="month" label="Month" />
</SegmentedControl>Uncontrolled — let the component manage its own state:
<SegmentedControl defaultValue="week">
<SegmentedControlItem value="day" label="Day" />
<SegmentedControlItem value="week" label="Week" />
<SegmentedControlItem value="month" label="Month" />
</SegmentedControl>Accessibility
The SegmentedControl component:
- Renders with
role="radiogroup"on the wrapper for semantic grouping - Each item renders with
role="radio"andaria-checkedreflecting selection state - Keyboard navigation: Arrow keys move between items (Left/Right for horizontal, Up/Down for vertical); Home/End jump to first/last
- Roving tabIndex: only the selected item (or first enabled item if none selected) is in the tab order
- Disabled items are skipped during keyboard navigation
- Touch targets ≥ 44×44px on all sizes
aria-labelrequired on icon-onlySegmentedControlItemelements- RTL fully supported via the
dirprop
Stepper
Numeric quantity control with increment and decrement buttons. Two layout variants (pill and floating), five sizes, full intent palette, min/max/step, disabled/readOnly states, and LTR/RTL support.
ButtonGroup
A group of pill buttons inside a rounded pill container, supporting horizontal and vertical orientations with configurable gap