A row of connected, mutually exclusive options backed by native radios — pick one from a small, always-visible set.

Last updated

Show 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 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 label shows a visible outline (peer-focus-visible).
  • No disabled state — the component doesn’t expose one (passthrough attributes land on the group container, not the individual inputs).
Show 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-label when 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 Select or Tabs instead.
  • 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”).