SwSegmented

SwSegmented is a single-select option group (segmented / pill-group): a row of { value, label } options that are all visible at once, picking one by click or arrow key. It's a controlled v-model, and emits update:modelValue whenever the selection changes.

It's a thin shell over reka-ui's RadioGroup, so arrow-key roving-tabindex accessibility (← → / Home End move between options and select them) works out of the box. Visuals follow a "minimal necessary baseline" — the active state defaults to a primary-color fill that's clearly distinguishable; colors, corner radius, and spacing are left to the applier to override.

When to use: reach for this component when you have few options (2–5), want them all visible, and want to save a click (filter pills / tabs / sorting). When there are many options, you need to save horizontal space, or a collapse-on-open trade-off is acceptable, use SwSelect (a dropdown). The two are complementary.

Basic usage

Basic single-select (sort switch)

加载中...
<script setup lang="ts">
const sort = ref('hot')
</script>

<template>
  <SwSegmented
    v-model="sort"
    :options="[
      { value: 'hot', label: '最热播' },
      { value: 'new', label: '最新上线' },
      { value: 'rating', label: '评分最高' },
    ]"
    aria-label="排序"
  />
</template>

Disabled state

Supports a per-item disabled (disabling a single option in the list) or disabling the whole group:

Per-item disabled / whole-group disabled

加载中...

Custom option appearance

The #option slot takes full control of a single option's content, with the scope exposing { option, selected, disabled }:

#option slot custom visuals

加载中...

Props

NameTypeRequiredDefaultDescription
modelValue (v-model)string | numberundefinedCurrent selected value (controlled). undefined means nothing is selected
optionsSwSegmentedOption[]✓—Option list; each item is { value, label, disabled? }
disabledbooleanfalseDisable the whole group

SwSegmentedOption: { value: string \| number; label: string; disabled?: boolean }

Events

NamePayloadDescription
update:modelValuevalue: string | numberSelection changed; the payload is the value of the selected option

Slots

NameScopeDescription
option{ option, selected, disabled }Customize a single option's visuals. Renders option.label when omitted

Styling Tokens

Brand the component via --sw-segmented-* tokens (fallbacks are the default look) — no :deep piercing needed:

TokenDefaultApplies to
--sw-segmented-gap0.25remgap between options
--sw-segmented-padding0.125remtrack padding
--sw-segmented-bgvar(--muted)track background (grey groove, the prevailing form)
--sw-segmented-bordertransparenttrack border color (1px width always present; set a color for an outlined track)
--sw-segmented-radiusvar(--radius)track radius
--sw-segmented-min-heightvar(--control-height)track min-height (form-control family)
--sw-segmented-font-sizevar(--control-font-size)option font size
--sw-segmented-option-padding0.125rem 0.875remoption padding
--sw-segmented-option-radiuscalc(var(--radius) - 0.25rem)option radius
--sw-segmented-option-colorvar(--muted-foreground)unselected text
--sw-segmented-hover-colorvar(--foreground)hover text for unselected options only (the selected one keeps its active color and dims via filter: brightness(0.92))
--sw-segmented-active-colorvar(--primary-foreground)active text
--sw-segmented-active-bgvar(--primary)active background
--sw-segmented-active-bordertransparentactive outline (inset box-shadow, zero reflow)
/* "Tinted + outlined" active style (instead of :deep piercing) */
.filter-bar {
  --sw-segmented-active-bg: color-mix(in srgb, var(--primary) 12%, transparent);
  --sw-segmented-active-color: var(--primary);
  --sw-segmented-active-border: var(--primary);
}

Default height / font size come from the form-control semantic tokens --control-height (32px) / --control-font-size (14px) — SwInput / SwSelect / SwSegmented align to the same height out of the box; override these two tokens to scale every control site-wide (component-level tokens still take precedence).

Caveats

  • No built-in visual branding: the component ships only the minimal necessary baseline styles (the active state is distinguishable); colors, corner radius, and spacing are customized via the --sw-segmented-* tokens above — no :deep selector piercing needed.
  • Arrow-key a11y is built in: ← → / Home End roving-move between options and select them, with no extra code needed.
  • Single-select only: this component does single-select only; for a collapsible dropdown or multi-select, use SwSelect.

See Also