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
| Name | Type | Required | Default | Description |
|---|---|---|---|---|
modelValue (v-model) | string | number | undefined | Current selected value (controlled). undefined means nothing is selected | |
options | SwSegmentedOption[] | ✓ | — | Option list; each item is { value, label, disabled? } |
disabled | boolean | false | Disable the whole group |
SwSegmentedOption: { value: string \| number; label: string; disabled?: boolean }
Events
| Name | Payload | Description |
|---|---|---|
update:modelValue | value: string | number | Selection changed; the payload is the value of the selected option |
Slots
| Name | Scope | Description |
|---|---|---|
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:
| Token | Default | Applies to |
|---|---|---|
--sw-segmented-gap | 0.25rem | gap between options |
--sw-segmented-padding | 0.125rem | track padding |
--sw-segmented-bg | var(--muted) | track background (grey groove, the prevailing form) |
--sw-segmented-border | transparent | track border color (1px width always present; set a color for an outlined track) |
--sw-segmented-radius | var(--radius) | track radius |
--sw-segmented-min-height | var(--control-height) | track min-height (form-control family) |
--sw-segmented-font-size | var(--control-font-size) | option font size |
--sw-segmented-option-padding | 0.125rem 0.875rem | option padding |
--sw-segmented-option-radius | calc(var(--radius) - 0.25rem) | option radius |
--sw-segmented-option-color | var(--muted-foreground) | unselected text |
--sw-segmented-hover-color | var(--foreground) | hover text for unselected options only (the selected one keeps its active color and dims via filter: brightness(0.92)) |
--sw-segmented-active-color | var(--primary-foreground) | active text |
--sw-segmented-active-bg | var(--primary) | active background |
--sw-segmented-active-border | transparent | active 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:deepselector 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.