Skip to content
omaris

Actions

Segmented Button

MD3 segmented button.

import { SegmentedButton } from 'omaris'
Learn

Examples

Basic

One choice out of a few, all visible at once. Items are data, not children.

<script lang="ts">	import { SegmentedButton } from 'omaris';​	let range = $state('week');</script>​<SegmentedButton	label="Range"	bind:value={range}	items={[		{ value: 'day', label: 'Day' },		{ value: 'week', label: 'Week' },		{ value: 'month', label: 'Month' }	]}/>

Multiple

multiple for a few that can all apply — Bold, Italic; it binds values and lets the set be empty.

<script lang="ts">	import { SegmentedButton } from 'omaris';​	let styles = $state<string[]>(['bold']);</script>​<SegmentedButton	multiple	mandatory={false}	label="Text style"	bind:values={styles}	items={[		{ value: 'bold', label: 'Bold' },		{ value: 'italic', label: 'Italic' },		{ value: 'underline', label: 'Underline' }	]}/>

Icons

A selected segment swaps its icon for the tick, unless keepIcon — which is what an icon-only set needs, or it loses its meaning when picked.

<script lang="ts">	import { SegmentedButton } from 'omaris';​	let view = $state('grid');	let align = $state('start');</script>​{#snippet grid()}	...{/snippet}{#snippet list()}	...{/snippet}​<div class="flex w-full flex-col items-center gap-4">	<SegmentedButton		label="View"		bind:value={view}		items={[			{ value: 'grid', label: 'Grid', icon: grid },			{ value: 'list', label: 'List', icon: list }		]}	/>​	<SegmentedButton		label="Alignment"		keepIcon		bind:value={align}		items={[			{ value: 'start', icon: list, ariaLabel: 'Align start' },			{ value: 'center', icon: grid, ariaLabel: 'Align centre' }		]}	/></div>

When to use it

Use it for

  • One choice out of two to five, all visible at once: Day/Week/Month, List/Grid, Sort by. Bind value; items are data, not children.
  • A few that can all apply — Bold, Italic; Show done, Show blocked: multiple binds values and lets the set be empty.
  • The view switcher above a Table or a Chart, where the choice changes what is shown now.
  • Icon-only segments — alignment, density — with keepIcon so the tick does not replace the meaning, and ariaLabel on each item.

Not for

  • More than five options → Select; options that are whole sections of the page → Tabs.
  • Filters that grow with the data, wrap, or can all be off → filter Chips.
  • A choice submitted with a form → Radio. A segment changes something now, not on Save.
  • One on/off → Switch, or a toggle Icon Button.
  • Commands that happen to sit together — Undo, Redo → Button Group. Segments are choices.

Do

  • Keep labels to a word or two of similar length; label truncates rather than wraps, and a long segment makes the row lopsided.
  • Pass label on the set — it names the radiogroup — and ariaLabel on any icon-only item.
  • Leave mandatory on for a view switcher. Turn it off only when "none" is a real state, such as an optional filter.
  • Use full on a phone so the segments share the width; size="sm" in a dense toolbar.

Don't

  • Mix labelled and icon-only segments in one set; the tick swaps the icon out and the widths jump.
  • Use a single-segment set as an on/off; that is a Switch.
  • Expect tone="destructive" to mean danger; tone here colours the selected fill, nothing more.

Quick reference

tone
  • primary (default)
  • secondary
  • tertiary
  • destructive
  • success
  • warning
  • info
size
  • sm
  • md (default)
  • lg
shape
  • round (default)
  • square

MD3's rounded rectangle instead of the pill.

API

SegmentedButton

MD3 segmented button.

A set of two to five related choices that reads as one control. Unlike ButtonGroup, which only lays buttons out, this owns the selection: it takes a value (or an array of them), reports changes, and renders MD3's leading tick on the chosen segments.

The tick slides in from zero width, so selecting a segment grows it rather than swapping its content.

import { SegmentedButton } from 'omaris'

Parts

Every element this renders is reachable from outside. Pass Tailwind for one part as classes={{ part: '…' }}; class covers the root and is merged last, so it always wins.

root
w-fit max-w-full, because inline-flex is not enough on its own and neither half of this is either.
segment
No description in the source yet.
check
The tick: zero-width until the segment is selected.
label
No description in the source yet.

Props

items required
SegmentedItem[]
value bindable

Defaults to undefined

string

Selected value, in single mode. Bindable.

values bindable

Defaults to []

string[]

Selected values, in multiple mode. Bindable.

multiple

Defaults to false

boolean

Allow more than one segment at a time.

tone

Defaults to 'primary'

SegmentedTone
primary
secondary
tertiary
destructive
success
warning
info
size

Defaults to 'md'

SegmentedSize
sm
md
lg
shape

Defaults to 'round'

SegmentedShape
round
square
full

Defaults to false

boolean
check

Defaults to true

boolean

Draw the leading tick on selected segments.

keepIcon

Defaults to false

boolean

Keep a segment's own icon instead of swapping it for the tick.

mandatory

Defaults to true

boolean

Refuse to clear the last selection.

ripple

Defaults to true

boolean
label
string

Accessible name for the set.

onchange
(value: string | string[]) => void
class
string
classes
SegmentedButtonClasses

Per-part Tailwind overrides. class still covers the root.