SegmentedControl
A row of connected, mutually exclusive options backed by native radios — pick one from a small, always-visible set.
Last updated
Show codeHide code
<SegmentedControl name="view" options={['List', 'Board', 'Calendar']} value="List" />Add
npx @ava-ui/apogee add segmented-control
No Apogee component dependencies.
Usage
---
import SegmentedControl from '@/components/apogee/SegmentedControl/SegmentedControl.astro'
---
<SegmentedControl name="view" options={['List', 'Board', 'Calendar']} value="List" />
API
@composable — exposed to Content Editors in AVA. Any other attribute
passes through to the root <div role="group">.
| Prop | Type | Default | Description |
|---|---|---|---|
name |
string |
— | Shared name for the underlying radio inputs. |
options |
(string | { label: string; value: string })[] |
— | Options, in order. A plain string is used as both label and value. |
value |
string |
— | Selected value. |
size |
'small' | 'medium' | 'large' | 'xlarge' |
'medium' |
Control size; the same height as a Button or Input of that size. |
aria-label |
string |
— | Accessible name for the group. |
class |
string |
— | Extra classes, merged last. |
Sizes
Every size, each beside a Button of the same size (they match in height):
Show codeHide code
<SegmentedControl name="size-sm" size="small" options={['List', 'Board', 'Calendar']} value="List" />
<SegmentedControl name="size-md" options={['List', 'Board', 'Calendar']} value="List" />
<SegmentedControl name="size-lg" size="large" options={['List', 'Board', 'Calendar']} value="List" />
<SegmentedControl name="size-xl" size="xlarge" options={['List', 'Board', 'Calendar']} value="List" />States
- Selected — the active option has a raised background (
bg-page,shadow-sm) and full-contrast text. - Hover — inactive options darken their text on hover.
- Focus — the focused option’s
labelshows a visible outline (peer-focus-visible). - No
disabledstate — the component doesn’t expose one (passthrough attributes land on the group container, not the individual inputs).
Show codeHide code
<SegmentedControl name="states" options={['One', 'Two', 'Three']} value="Two" />Examples
Options as { label, value }, when the display text and submitted value
differ:
<SegmentedControl
name="sort"
options={[
{ label: 'Newest', value: 'created_desc' },
{ label: 'Oldest', value: 'created_asc' },
]}
value="created_desc"
/>
Accessibility
| Key | Action |
|---|---|
Tab |
Moves focus into the group (one stop, on the checked option). |
← → ↑ ↓ |
Move between options and select (native radio behaviour). |
- Built on native radio inputs inside a
role="group". - Each option’s visible label is a real
<label for>, not decorative text. - Give the group an
aria-labelwhen no visible heading identifies what it controls.
Tokens
| Token | Used for |
|---|---|
--border-neutral |
Track border |
--background-neutral-faded |
Track background |
--background-page |
Selected option fill |
--foreground-neutral-faded |
Unselected label |
--foreground-neutral |
Selected / hovered label |
--outline-color-focus |
Focus outline |
Do / Don’t
- Do use SegmentedControl for 2–5 mutually exclusive, always-visible options where seeing every choice at once helps (view modes, a small set of filters).
- Do keep labels to one word when possible — the control reads best as a compact bar.
- Don’t use it for more than ~5 options — it wraps ungracefully; use
SelectorTabsinstead. - Don’t use it as a multi-select — one radio group, one value.
Content
- Labels are short nouns or verbs, sentence case, no ending punctuation (“List”, “Board”, not “List view”).