KQ-1512 · STRETCH/TABS

Stretch Tabs

A segmented control whose pill is rubber: its two edges are separate springs, so choosing another segment sends the leading edge out first to land with a slight overshoot while the trailing edge holds for a beat and then lets go on a softer spring — the pill stretching toward the new segment, thinning as it stretches, and squashing against the track's ends instead of leaving them. The selected label colour is a copy of the labels clipped to the pill's exact shape, so it sweeps across each letter with the rubber edge; at elastic 0 the pill is one piece on springs.snap. The pill can be dragged 1:1 (its trailing edge following on a short spring) and a release projects the throw and snaps to the nearest segment with the release velocity; arrows move and wrap, Home and End jump, in tablist or radiogroup semantics with a roving tabindex.

KQ-1512 · STRETCH/TABS

Basin BSN

421.15+2.2%

range 1 month · +2.2%

Install

One command — the source lands in your repo. Or copy it from the Code tab.

$ pnpm dlx shadcn@latest add @kinetiq/stretch-tabs

Props

items
StretchTabsItem[]
default —
The segments, in order: { value, label, ariaLabel?, controls?, disabled? }. `controls` is the id of the panel a tab shows.
value / defaultValue
string
default — / the first item
The selected value, controlled or initial.
onValueChange
(value: string) => void
default —
Fires from the press, key or drag that changed it, with the new value.
label
string
default —
The control's accessible name.
segments
number
default items.length
How many of the items are shown, from the first; the pill keeps its place in segment units, so a change needs no measuring.
elastic
number
default 0.5
The rubber, 0 to 1: how long the trailing edge holds, how soft it catches up, how far the pill may stretch (1.5 to 2.6 segments), how much it thins, and how much the leading edge overshoots. 0 is one rigid piece on springs.snap.
size
"sm" | "md" | "lg"
default "md"
Track height 32, 36 or 44px with 12, 13 or 14px labels.
semantics
"tablist" | "radiogroup"
default "tablist"
Tabs that show panels (role tab, aria-selected, aria-controls) or a radio group that picks a value (role radio, aria-checked).
activation
"auto" | "manual"
default "auto"
Arrow keys select as they move, or (tablist only) only move focus until Enter or Space.
draggable
boolean
default true
The pill can be dragged along the track; a drag that starts on another segment selects the segment it is released over.
pill
string
default "var(--foreground)"
The pill's colour, any CSS colour.
pillText
string
default "var(--background)"
The selected label's colour, on the pill.
sound
boolean
default false
A tick on every change and on every segment a drag crosses, pitched by position; only in answer to the visitor.
disabled
boolean
default false
Disables every segment.
className
string
default —
Classes for the track; it is inline-grid sized by its widest label, so pass w-full to stretch it.