dssoca docs
GitHub repository

SegmentedControl

Compact row of mutually-exclusive options.

A compact, inline toggle for picking one of a few options (view, mode, locale). The selected segment is highlighted with a token-driven fill (zero radius), and the group behaves as a single radiogroup: one tab stop, arrow / Home / End keys move and select, disabled options are skipped.

Demo

live · storybook Open in Storybook

Demo loads from https://dssoca-storybook.vercel.app/. Run pnpm storybook for it to render locally.

Usage

svelte
<script>
  import { SegmentedControl } from 'dssoca';
  let view = $state('grid');
</script>

<SegmentedControl
  label="View mode"
  options={[
    { value: 'list', label: 'List', icon: 'logs' },
    { value: 'grid', label: 'Grid', icon: 'grid' },
    { value: 'map', label: 'Map' },
  ]}
  bind:value={view}
/>

WCAG 2.2 AA: `role="radiogroup"` with `role="radio"` segments, roving tabindex (one tab stop), and arrow / Home / End navigation that moves focus and selection together while skipping disabled segments. A `label` is required so the choice has an accessible name. For switching mutually-exclusive views/panels, a tablist/tab + tabpanel pattern is the alternative; this component implements the radiogroup pattern for value selection.

Props

PropTypeDefaultDescription
optionsSegmentOption[][]Mutually-exclusive options: { value, label, icon?, disabled? }.
valuestringSelected option value (bindable).
labelstringAccessible name for the group (required → aria-label on the radiogroup).
onChange(value: string) => voidFired when the selection changes, after `value` updates.
fullWidthbooleanfalseStretch segments to equal widths and fill the container.
disabledbooleanfalseDisable the whole control: every segment is rendered inert (disabled, skipped by keyboard nav, no tab stop) and the group carries aria-disabled="true". Per-option `disabled` still disables individual segments.
size'sm' | 'md' | 'lg'Per-instance size override; inherits the ancestor `data-size-variant` when unset.