Skip to content
buttery

v1.0.0

Source on GitHub

Version 1.0.0

SegmentedControl

One choice out of a few, shown side by side in a track, with the selection sliding between them.

import { SegmentedControl } from "@butteryui/react";

Demo

When to use it

Use it for

  • Two to five options that are all worth seeing at once: Gradient or Solid, Fit or Fill
  • Switching a view where one option is always on
  • A control that should span its column, as in a form card, with layout="fill"; a toolbar or a dense panel with size="sm"; a row of 48px fields or buttons with size="lg"

Not for

  • More than five options or long labels: use Select
  • Options that can all be off, or several on: use toggle buttons
  • Navigation between pages: use tabs or links

Examples

Aspect picker

A deck's slide shape, with labels that differ from the values the control reports.

Every slide is 4:3

Export quality

A disabled option and a disabled control: WebP isn't on this plan, and quality only applies to JPG.

WebP comes with the Team plan.

PNG keeps every pixel, so quality doesn't apply

Fill switch

Gradient or solid fill for a slide background, kept in state so the swatches follow it and Reset can set it back.

Showing gradient swatches.

Sizes and fill

A small control in a header beside a full-width one that spans its form with layout="fill".

Your subscription

A 12 oz bag every 2 weeks

View switch

A deck's slides with an icon-only view switch in its toolbar, each icon's label in its tooltip and name.

Quarterly review

Showing 4 slides as a list

  • Title slide
  • Agenda
  • Q3 in numbers
  • Thank you

Variants and states

Two options

Click a segment or use the arrow keys: the selection slides, and one is always chosen.

Showing gradient swatches.

Equal widths, a disabled option

Sizes

md, the default, is 34px tall (30 compact); sm is 28 for a toolbar or a dense panel; lg is 48 (40 compact), its segments 4px inside the track, beside 48px buttons and fields.

Hug and fill

hug, the default, keeps the track as wide as its segments, even in a Stack that stretches its children; fill spans the width it's given, its segments sharing it equally.

Props

SegmentedControl

PropTypeDefaultDescription
options

required

readonly (Value | SegmentedControlOption<Value>)[]The choices, as values that are also their labels or as { value, label, disabled }.
valueValueThe chosen value. Pair with onValueChange to control it.
defaultValueValueThe value chosen at first when uncontrolled. Defaults to the first option.
onValueChange((value: Value) => void)Called with the new value when the user picks another segment.
label

required

stringWhat the choice is about, for screen readers, such as "Picture fit".
disabledbooleanKeeps every segment from being chosen.
iconOnlybooleanShows each option's icon in place of its label, which becomes its tooltip and name. Give every option an icon.
size"sm" | "md" | "lg""md"The track's height: "md" (34px, 30 compact, the default), "sm" (28px) for a toolbar or a dense panel, or "lg" (48px, 40 compact) beside 48px buttons and fields.
layout"hug" | "fill""hug""hug" (the default) keeps the control as wide as its segments wherever it's placed, even in a Stack or a grid cell that stretches its children; "fill" spans the width it's given, its segments sharing it equally.

It also takes Omit<ComponentPropsWithRef<"div">, "defaultValue" | "onChange" | "dir">.

SegmentedControlOption

One option of a `SegmentedControl`.

FieldTypeDefaultDescription
value

required

ValueWhat value and onValueChange carry.
label

required

stringWhat the segment says.
disabledbooleanKeeps the segment from being chosen.
iconComponentType<IconComponentProps>An icon before the label, or in its place with iconOnly.

Accessibility

A radio group named by label: Tab reaches the chosen segment, arrow keys move the choice and pick as they go, and one segment is always chosen.

KeysWhat they do
TabReaches the chosen segment, the control's one Tab stop
Left or Right arrowMoves the choice to the segment before or after, going round at the ends and skipping disabled ones
Home or EndMoves focus to the first or last segment, which Space then chooses
SpaceChooses the segment with focus

Motion

Its selection indicator slides to the chosen option on `spring.snappy`, inside one track.

With reduced motion: The selection appears on the chosen segment at once instead of sliding.

Layout

A pill track 34px tall (--bt-h-control, 30 compact) at size="md", 28px (--bt-h-sm) at "sm" or 48px (--bt-h-cta, 40 compact) at "lg", padded 2px (4px at "lg") around segments that share one width, the widest label's. With layout="hug", the default, it's as wide as that makes it wherever it's placed, a Stack or a grid cell that stretches its children included; with layout="fill" it spans the width it's given, its segments sharing it equally, and in an Inline it takes the room its siblings leave. It's never wider than its container, where a label that doesn't fit truncates with an ellipsis and shows in full as its title. Each segment's full height is its hit area.

See it in the roastery demo's orders. See it in the Halfmoon shop demo. See it in the Deckhand demo.