Segmented control

One choice from a few short options shown side by side, such as a message header type (No header / Text / Media / Location). For more options or longer labels, use a Radio group or a Select.

Installation

bun add @harmo-ui/css @harmo-ui/react

Usage

import { Segmented, SegmentedOption } from "@harmo-ui/react";

<Segmented label="Message header" name="header-type" defaultValue="none">
  <SegmentedOption value="none">No header</SegmentedOption>
  <SegmentedOption value="text">Text</SegmentedOption>
  <SegmentedOption value="media">Media</SegmentedOption>
  <SegmentedOption value="location">Location</SegmentedOption>
</Segmented>

Examples

This site is using your theme

Options

Each option wraps a native radio, so the group works in forms, Tab reaches the chosen option, and the Arrow keys move the choice. role="radiogroup" names the group from its label.

Message header
<Segmented label="Message header" name="header-type" defaultValue="none">
  <SegmentedOption value="none">No header</SegmentedOption>
  <SegmentedOption value="text">Text</SegmentedOption>
  <SegmentedOption value="media">Media</SegmentedOption>
  <SegmentedOption value="location">Location</SegmentedOption>
</Segmented>

Disabled option

disabled on an option's radio dims that option.

Assign to
<Segmented label="Assign to" name="assign" defaultValue="team">
  <SegmentedOption value="team">Team</SegmentedOption>
  <SegmentedOption value="agent">Agent</SegmentedOption>
  <SegmentedOption value="ai" disabled>
    AI agent
  </SegmentedOption>
</Segmented>

Narrow container

Labels never wrap. When the options can't all fit on one row, drawn dashed here, they move to a new one.

Message header
<Segmented
  label="Message header"
  name="header-type-narrow"
  defaultValue="none"
>
  <SegmentedOption value="none">No header</SegmentedOption>
  <SegmentedOption value="text">Text</SegmentedOption>
  <SegmentedOption value="media">Media</SegmentedOption>
  <SegmentedOption value="location">Location</SegmentedOption>
</Segmented>

API reference

React

<Segmented> renders the group, with a <SegmentedOption> for each option; with label, it also renders the Field and label above it. Without label, name it with aria-label, or place it in a Setting, which names it. Other props are the group <div>'s attributes.

PropEffect
labelThe label above it
nameThe name its options submit under; generated when left out
value or defaultValueThe chosen option's value
onValueChangeCalled with the newly chosen value
required, disabledSet on every option

<SegmentedOption> takes its value, its text as children, and optionally disabled. Its className and style go to the option's <label>, and its other props to the radio inside.

HTML

A <div class="hsegmented" role="radiogroup"> with aria-labelledby or aria-label. Each .hsegmented-option is a <label> wrapping an <input type="radio"> and its text, with the same name on every radio. Load segmented.css; field.css for the label. It needs no script.

HookEffect
checked on a radioThe chosen option
disabled on a radioThat option dimmed, not-allowed cursor

See Segmented control's full contract.

Accessibility

Each option wraps a native radio, so the group works in forms, Tab reaches the chosen option, and the Arrow keys move the choice. The radios are invisible but stay focusable, and role="radiogroup" with aria-labelledby names the group.