Skip to content
TopClass UI

Segmented control

One choice out of two to five short ones, drawn as one track. It is a radio group: the arrow keys select as they move, and reverse in RTL.

client - size not measured yet

Segmented control

client - size not measured yet

One choice out of two to five short ones, drawn as one track. It is a radio group: the arrow keys select as they move, and reverse in RTL.

Install

Try it

size
fullWidth
invalid

The code

segmented-control.tsx
import { SegmentedControlItem, SegmentedControlRoot } from 'topclassui/segmented-control'

<SegmentedControlRoot aria-label="View" defaultValue="board">
  <SegmentedControlItem value="board">Board</SegmentedControlItem>
  <SegmentedControlItem value="table">Table</SegmentedControlItem>
  <SegmentedControlItem value="calendar">Calendar</SegmentedControlItem>
</SegmentedControlRoot>

The arrow keys move and select at once; under an RTL TopClassProvider, ArrowRight goes to the segment before.

Every state

4 states

Import from the package

topclassui/segmented-control
import { SegmentedControlRoot, SegmentedControlItem } from 'topclassui/segmented-control'

Install once:npm i topclassui@beta zod

Examples

  • For one choice out of two to five short ones: a view, a mode, a range. One segment is always selected; for longer choices, a RadioGroup reads better.
  • Two parts, SegmentedControlRoot and SegmentedControlItem, and it runs in the browser. A server page passes defaultValue alone; a controlled value belongs in a client file.
  • The track needs a name and a selected segment: pass aria-label or aria-labelledby, and value or defaultValue. The types refuse a track without them.
  • invalid sets aria-invalid on the track, whose line turns red and thickens. fullWidth fills the parent and shares it equally, for a few short segments.
  • When the segments do not fit, the track scrolls sideways inside itself and the page does not; a label longer than its segment ends in an ellipsis.
  • icon on a segment is drawn before its label. It is decoration: the label names the segment.
basic
with-icons
sizes
full-width
Range
form
Default view
many
long-content
In Arabic, right to left
طريقة العرض

Props

SegmentedControlRoot

The props of SegmentedControlRoot
PropTypeDefaultLimitsDescription
aria-labelstring--The name read out when no visible text names the group.
aria-labelledbystring--The id of the visible text that names the group.
defaultValuestring--The segment selected when the page opens, and after a form reset.
valuestring--The selected segment, held by its owner.
fullWidthbooleanfalse-SegmentedControlRoot: the track fills its parent and its segments share it equally. For a few short segments.
invalidbooleanfalse-SegmentedControlRoot: marks the choice as wrong: sets aria-invalid on the track, whose line turns to danger and thickens.
refRef<HTMLDivElement>--The track, the element with role="radiogroup".
sizexs | sm | md | lg | xl | custom"md"-SegmentedControlRoot: the height of the track and the padding and type step of every segment; custom emits none.

SegmentedControlItem

The props of SegmentedControlItem
PropTypeDefaultLimitsDescription
value*string--The string the form receives, and what value and onValueChange of the root carry.
iconReactNode--Drawn before the label, at the size of its own. Decoration: the label names the segment.
refRef<HTMLButtonElement>--The segment itself, which is a <button>.
styleCSSProperties--

Keyboard and accessibility

The keyboard of SegmentedControl
KeysWhat it does
TabEnters the track on the selected segment; the next Tab leaves it.
ArrowRightMoves to the next segment and selects it; in RTL, the segment before.
ArrowLeftMoves to the segment before and selects it; in RTL, the next one.
ArrowDownMoves to the next segment and selects it.
ArrowUpMoves to the segment before and selects it.
SpaceSelects the focused segment.
  • The track is a radiogroup; each segment is a native <button> with role="radio", named by its own text. The arrows skip a disabled segment and wrap at both ends; Enter, Home and End do nothing.
  • Before the page hydrates, the selected segment is already the one tab stop, so Tab lands on it from the first paint.
  • Each segment is at least 24 px tall, 44 under a touch pointer. The focus ring is drawn inside the focused segment, and under forced colours the selected one takes the system highlight.

Right to left

The first segment sits at the right, and the arrow keys reverse with the direction TopClassProvider passes from your layout.

Server and client

A client component: it renders on the server and hydrates in the browser.

Files: index.tsx, client.tsx.

On the chip: client - size not measured yet.