SegmentedControl
A list of buttons for switching one view mode, with the selected option marked by aria-current.
Implementation previewEvery example. In one place.
Explore every named example, including states, compositions, and theme variants.
Example source
packages/design-system/src/components/controls/SegmentedControl/SegmentedControl.stories.tsxExport PlaygroundImplementation preview Local Storybook build 0.2.0-preview.1. Source specification status does not approve this runtime.
Stories + source JSON ↗Local specification source status: preview. Implementation preview pending sign-off.
A list of buttons for switching one view mode, with the selected option marked by aria-current.
Implementation API 1 entry point · source-derived preview
Read the actual callable API and preserve its composition constraints. Each union branch is a complete alternative; props marked forbidden cannot be supplied.
import { SegmentedControl } from '@cloakedinc/design-system';SegmentedControlSegmentedControlProps
packages/design-system/src/components/controls/SegmentedControl/SegmentedControl.tsx:63
| Prop | Type | Presence | Notes |
|---|---|---|---|
items | SegmentedControlItem[] | Required | Options rendered in order. Keep the set to 5 or fewer, per the iOS guideline. |
activeId | string | Required | Controlled `id` of the active option. An id matching no item leaves none active. |
onSelect | ((id: string) => void) | undefined | Optional | Called with the selected option id. Skipped when the pressed option is already active. |
ariaLabel | string | undefined | Optional | Accessible name for the strip, announced before the options — such as `Time range`. |
className | string | undefined | Optional | |
haptic | HapticFeedback | undefined | Optional | Haptic feedback for selection changes. Defaults to `true` (`selection`).Source default: true |
sizing | SegmentedControlSizing | undefined | Optional | How options claim width. `fill` (default) shares the strip equally and
ellipsises labels that do not fit; `hug` sizes each option to its own
label, for a strip that should be no wider than its content.Source default: 'fill' |
testId | string | undefined | Optional | Optional test selector. Each option derives `${testId}-${id}`. |
Only explicit destructured parameter initializers are extracted. Other runtime defaults remain in implementation source.
Overview
An iOS-style single-select switch for up to 5 mutually exclusive modes that re-filter one view in place, such as a time range or a chart granularity.
It is a list of buttons
The markup is a <ul> of buttons, and the selected one carries aria-current. It is deliberately
none of the roles it resembles:
- Not a radiogroup. That role needs radio semantics on every option —
role="radio"witharia-checked— plus radio-group arrow-key behavior. These are ordinary buttons. - Not a tablist. That role promises content panels and arrow-key traversal.
- Not a toolbar. That role is a collection of function buttons.
Each of those roles carries its own keyboard and announcement rules, so borrowing the wrong one misleads anyone using assistive technology.
When to use
Reach for SegmentedControl when a small, fixed set of options changes how the same content is
sliced and all options fit inline. Choosing between the neighbours:
- The options switch between distinct content panels rather than re-filtering one view? Use
Tabs. - The set grows from data, wraps, or scrolls? Use a
ChipGroup. The boundary is the shape of the set: a segmented control is fixed, connected, and equal-width. - A binary input, such as on and off? Use
Toggle. - You want a labelled form field with descriptions, or more than 5 options? Use
RadioGroup. - Too many options to sit inline? Use
Dropdown+Menu.
Counts and dots
An option may carry a trailing count, a decorative dot, or its own disabled state. A count is
text, so it is announced with the label. A dot is not, so put the reason it is there into the label
when it carries meaning.
Component specification
$schema
../../../specs/component-spec.schema.jsonParts
- Id
- strip
- Label
- Strip
- Description
- The list that owns the accessible name and shares width equally between options.
- Required
- true
- Id
- option
- Label
- Option
- Description
- One button per option. Carries aria-current and its own disabled state.
- Required
- true
- Id
- count
- Label
- Count
- Description
- Optional trailing number, rendered as text so it is announced with the label.
- Id
- dot
- Label
- Dot
- Description
- Optional decorative attention indicator.
Allowed Design Choices
- Variants
- Pill
- Label
- Pill
- Description
- Equal-width pill segments with filled active and outlined inactive treatments.
- Sizes
Variants
- Id
- pill
- Label
- Pill
- Description
- Compact mode switch with one active option.
- Platforms
- ios
- android
- web
Sizes
None specifiedMeasurements
- Id
- option-height
- Label
- Option height
- Kind
- exact
- Token
- --cds-size-32
- Value
- 32
- Unit
- px
- Applies To
- option
- Id
- option-horizontal-padding
- Label
- Option horizontal padding
- Kind
- exact
- Token
- --cds-space-16
- Value
- 16
- Unit
- px
- Applies To
- option
- Id
- option-content-gap
- Label
- Option content gap
- Kind
- exact
- Token
- --cds-space-4
- Value
- 4
- Unit
- px
- Applies To
- option
- count
- dot
- Id
- option-gap
- Label
- Gap between options
- Kind
- exact
- Token
- --cds-space-8
- Value
- 8
- Unit
- px
- Applies To
- strip
- Id
- dot-size
- Label
- Dot size
- Kind
- exact
- Token
- --cds-size-8
- Value
- 8
- Unit
- px
- Applies To
- dot
Layout
- Alignment
- Options share equal width and center their content in both axes.
- Flow
- Two to four options form one horizontal row.
- Notes
- Use Tabs when options switch distinct content panels rather than one view mode.
- Use a ChipGroup when the set grows from data, wraps, or scrolls.
- Use Toggle for a binary input rather than a two-option strip.
Typography And Color
- Typography
- Label
- Segment label
- Description
- Use the compact link ramp for concise mode labels.
- Token
- --cds-type-link-size
- Colors
- Label
- Active segment
- Description
- Use the bold semantic surface.
- Token
- --cds-bg-bold
- Label
- Active label
- Description
- Use text intended for bold surfaces.
- Token
- --cds-text-on-bold
- Label
- Inactive label
- Description
- Use primary text emphasis.
- Token
- --cds-text-primary
- Label
- Inactive border
- Description
- Use the subtle surface stroke.
- Token
- --cds-surface-subtle
States
- Id
- active
- Label
- Active
- Description
- Bold fill with on-bold text, marked by aria-current.
- Id
- inactive
- Label
- Inactive
- Description
- Transparent fill with a subtle border.
- Id
- disabled
- Label
- Disabled
- Description
- One option dimmed and blocked through the native disabled attribute.
- Id
- with-count
- Label
- With count
- Description
- Shows a trailing number after the label.
- Id
- with-dot
- Label
- With dot
- Description
- Shows a trailing attention indicator.
Behavior
- Label
- Select on press
- Description
- Invoke onSelect with the pressed option id. Skip it when that option is already active.
- Label
- Per-option disable
- Description
- An option may be disabled on its own through the native attribute, without disabling the set.
- Label
- Bounded set
- Description
- Keep the set to 5 or fewer options, per the iOS guideline. Past that, reach for a Chip group or a Select.
Accessibility
- Label
- Not a radiogroup
- Description
- A radiogroup needs radio semantics on every option, role=radio with aria-checked, plus radio-group arrow-key behavior. These are ordinary buttons, so the selected one carries aria-current.
- Label
- Not a tablist
- Description
- A tablist promises content panels and arrow-key traversal. Reach for Tabs when the options switch panels.
- Label
- Explicit list role
- Description
- The list keeps role=list because removing the bullets strips list semantics in some engines.
- Label
- Group name
- Description
- Pass ariaLabel so the set is announced before its options.
- Label
- Counts over dots
- Description
- A count is text and announces. A dot is decorative, so say why it is there in the label when it carries meaning.
Content Adaptation
- Label
- Option count
- Description
- Keep the set to two through four mutually exclusive modes.
- Label
- Concise labels
- Description
- Use parallel, short labels that remain legible at equal widths.
- Label
- Width sizing
- Description
- sizing='fill' (default) shares the strip equally and ellipsises a label that does not fit. sizing='hug' sizes each option to its own label, so an auto-width strip is never narrower than its content; use it when the strip sits beside other content rather than filling a row.
Motion Haptics
- Motion
- Label
- Selection treatment
- Description
- Swap active and inactive color treatments without moving or resizing segments.
- Haptics
- Label
- Selection feedback
- Description
- Request the selection haptic after the active segment changes.
Platform Differences
- Platforms
- web
- Description
- Web renders no focus ring and no arrow-key traversal between options.
- Rationale
- The consumers are a touch iOS WKWebView and a harness previewing it, and per Primer arrow keys must not move selection in a segmented control anyway.