Skip to main content

Segmented Control

Info:Controls html svelte astro Success: coverage 67/67

A single-select toggle bar: pick one of a few options from a connected button group.

Live demo

live · @xoji/astro

Segmented Control

Choices worth showing at once

View
Alignment
Theme

Sizes & disabled

Small
Large
Locked

Every tone

Accents

accent
accent-2
accent-3
accent-4
neutral

Statuses

success
info
warn
danger

Named hues

red
orange
yellow
green
blue
purple
brown
pink
cyan
gray
white
black

Segmented Control picks one option from a small, fixed set rendered as a connected button bar. It's the compact alternative to a radio group when the choices are few and worth showing at once.

It is a role="radiogroup" of role="radio" buttons with roving tabindex: the selected segment is the tab stop, arrow keys move and select with wraparound, and Home/End jump to the ends. Options are declared as a comma-separated options string, either bare labels (the label is the value) or label:value pairs. It's form-associated; give it a name and the chosen value submits. Three sizes: sm, md, lg.

When to use

How this component composes with the rest of the set.

Reach for it over a radio group when there are two to five options worth showing at once: view switches, alignment, light/dark/auto.
For many options, a long list, or free text, use Select instead.
Pair with a form and a name so the chosen value submits like any field.

Props

8 props, straight from the manifest.

PropTypeDefaultBindingsDescription
value string
html svelte astro
The selected option's value. Defaults to the first option. Reflected and form-submitted.
options string
html svelte astro
Comma-separated options: bare labels (`Day,Week`) or `label:value` pairs (`Left:start`).
disabled boolean false
html svelte astro
Disables selection and mutes the bar.
size Size
sm md lg
md
html svelte astro
Control size: `sm`, `md`, or `lg`.
tone FullTone
accent neutral danger success warn info accent-2 accent-3 accent-4 red orange yellow green blue purple brown pink cyan gray white black
accent
html svelte astro
Color of the selected segment's fill. Any semantic role, accent variant, or named hue.
label string
html svelte astro
Visible label, also the accessible name via `aria-labelledby`.
labelledby string
html svelte astro
ID of an external element that names the group. Takes precedence over `label`.
name string
html svelte astro
Form field name; the selected value submits with the form.

Appearance

Sizes

sm

.xoji-segmented--sm

Compact.

md

default
.xoji-segmented

Default.

lg

.xoji-segmented--lg

Large.

States

selected

.xoji-segmented__option[aria-checked="true"]

The chosen segment: filled with the accent and its readable foreground. When the theme's --selection-cue resolves to marker (a high-contrast or redundant-cues algorithm), the selected segment gains a non-color check glyph so selection never rests on color alone.

hover

.xoji-segmented__option:hover::after

Pointer over an unselected segment; an overlay paints the hover tint.

focus-visible

.xoji-segmented__option:focus-visible

Keyboard focus on a segment: a token ring plus a transparent outline that becomes real in forced-colors mode.

disabled

.xoji-segmented--disabled

The whole group disabled: muted, non-interactive.

Anatomy

The named parts that make up the component, with their selectors.

field

.xoji-segmented-field

The wrapper stacking the optional label over the toggle bar.

--font-sans --space-2

segmented

.xoji-segmented

The role="radiogroup" track holding the options.

--space-1 --bg-1 --border-thin --line-2 --radius-md

option

.xoji-segmented__option

Each role="radio" segment; the selected one fills with the accent.

--fg-1 --fg-0 --text-sm --text-body --weight-medium --space-1 --space-2 --space-3 --space-4 --radius-sm --accent --accent-fg --state-hover --state-press

label

.xoji-segmented__label

The optional visible label, referenced as the group's accessible name.

--fg-1 --text-sm

Tokens & coverage

What the component consumes, checked live against what the algorithm produces.

Success:fully covered 67/67 consumed tokens produced default register: 276 tokens

Live coverage check against the xoji-default register (derive(xojiDefault, { anchors })coverComponent(manifest, register)). Every token this component consumes must be a key the algorithm produces.

--accent --accent-2 --accent-2-fg --accent-3 --accent-3-fg --accent-4 --accent-4-fg --accent-fg --bg-1 --black --black-fg --blue --blue-fg --border-normal --border-thick --border-thin --brown --brown-fg --cyan --cyan-fg --danger --danger-fg --duration-fast --ease-standard --fg-0 --fg-1 --fg-disabled --font-sans --gray --gray-fg --green --green-fg --info --info-fg --line-2 --neutral --neutral-fg --orange --orange-fg --pink --pink-fg --purple --purple-fg --radius-md --radius-sm --red --red-fg --ring --selection-cue --space-1 --space-2 --space-3 --space-4 --state-disabled --state-hover --state-press --success --success-fg --text-body --text-sm --warn --warn-fg --weight-medium --white --white-fg --yellow --yellow-fg

Accessibility

The bar is role="radiogroup" and each segment role="radio" with aria-checked, never conveying selection by color alone.
Roving tabindex: the selected segment is the single tab stop; arrow keys move and select with wraparound, Home/End jump to the ends, click selects.
Requires an accessible name: labelledby wins, then label; the binding warns at runtime when neither is present.
Focus is shown with a token ring and a transparent outline the forced-colors base rule promotes to a real system outline.
Selection carries a non-color channel on demand: when the theme sets --selection-cue: marker, the selected segment gains a check glyph alongside the color fill, satisfying WCAG 1.4.1. High-contrast emits marker by default, and any algorithm can opt in via the cues knob.
disabled blocks selection, drops the segments out of the tab order, and mutes the bar.

Code

Labels, value pairs, and sizes

A view switch, an alignment control using label:value pairs, the compact size, and a disabled group.

<xoji-segmented label="View" options="Day,Week,Month" value="Week"></xoji-segmented>

<xoji-segmented label="Align" options="Left:start,Center:center,Right:end" value="center"></xoji-segmented>

<xoji-segmented size="sm" label="Theme" options="Light,Dark,Auto" value="Auto"></xoji-segmented>

<xoji-segmented label="Locked" options="One,Two,Three" value="Two" disabled></xoji-segmented>
<script lang="ts">
	import { Segmented } from "@xoji/svelte";

	let view = $state("Week");
</script>

<Segmented label="View" options="Day,Week,Month" bind:value={view} />

<Segmented label="Align" options="Left:start,Center:center,Right:end" value="center" />

<Segmented size="sm" label="Theme" options="Light,Dark,Auto" value="Auto" />

<Segmented label="Locked" options="One,Two,Three" value="Two" disabled />
---
import { Segmented } from "@xoji/astro";
---

<Segmented label="View" options="Day,Week,Month" value="Week" />

<Segmented label="Align" options="Left:start,Center:center,Right:end" value="center" />

<Segmented size="sm" label="Theme" options="Light,Dark,Auto" value="Auto" />

<Segmented label="Locked" options="One,Two,Three" value="Two" disabled />