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.

HTML

interactive with vanilla.js The exact markup the component renders, generated at build time. Use it with dssoca/vanilla.css+ dssoca/vanilla.js — see Plain HTML & CSS.

html
<div class="ss-segmented" role="radiogroup" aria-label="Range">
  <button type="button" class="segment selected" role="radio" aria-checked="true" tabindex="0" data-value="day">
    <span class="label">Day</span>
  </button>
  <button type="button" class="segment" role="radio" aria-checked="false" tabindex="-1" data-value="week">
    <span class="label">Week</span>
  </button>
  <button type="button" class="segment" role="radio" aria-checked="false" tabindex="-1" data-value="month">
    <span class="label">Month</span>
  </button>
</div>

vanilla.js moves the selection (click, arrows, Home/End) and emits ss:change with the segment's data-value.

Props

PropTypeDefaultDescription
optionsSegmentOption[][]Mutually-exclusive options: { value, label, icon?, disabled? }.
valuestring—Selected option value (bindable).
labelstring—Accessible name for the group (required → aria-label on the radiogroup).
onChange(value: string) => void—Fired 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.
↑↓ navigate · ↵ open · esc close

Keyboard shortcuts

No shortcuts registered

Shortcuts added through the dssoca registry will show up here.

Single-key shortcuts