KQ-1500 · RAIL/SELECT
Rail Select
A select whose open list has a rail down its side with a node per option, like a line on a transit map, and whose highlight is one shape — a wash across the row and a car on the rail — with its top and bottom edges on separate springs: the leading edge leaves on a stiff spring and the trailing edge follows on a softer, later one, so the highlight stretches toward the next stop before its tail catches up. Choosing rolls the trigger's value to the new label on springs.snap, out the way the list moved, while the rail draws down on springs.glide each time the list opens. It is an APG select-only combobox: focus stays on the trigger while arrows, Home, End, Page keys and type-ahead move the highlight, Enter, Space or Tab choose, and Escape closes.
- Latency
- 18 ms
- Failover
- Fjord North
- Data stays in
- Harbor
- Cache
- Warm
region · harbor east · 18 ms
Install
One command — the source lands in your repo. Or copy it from the Code tab.
$ pnpm dlx shadcn@latest add @kinetiq/rail-select
Props
- options
- { value: string; label: string; hint?: string; disabled?: boolean }[]
- default —
- The stops on the rail, top to bottom; a hint sits at the row's end, and disabled options are skipped by every key.
- value / defaultValue
- string | null
- default — / null
- The chosen option's value, controlled or uncontrolled.
- onValueChange
- (value: string) => void
- default —
- Fires from the click or key that chose an option.
- open / defaultOpen
- boolean
- default — / false
- Whether the list is open, controlled or uncontrolled.
- onOpenChange
- (open: boolean) => void
- default —
- Fires from the press, key or outside press that opened or closed it.
- label
- string
- default —
- What is being chosen: the caption above the trigger and the list's accessible name.
- hideLabel
- boolean
- default false
- Keep the caption for assistive technology only.
- placeholder
- string
- default "Choose"
- The trigger's text before anything is chosen.
- stretch
- number
- default 0.5
- How far the highlight stretches toward the next option before its tail catches up, 0 to 1: the trailing edge's spring softens and lags as it rises.
- speed
- number
- default 0.5
- How quick the ride is, 0 to 1: the leading edge's stiffness, from a heavy car to a light one.
- width
- number
- default 240
- The trigger's and the list's width in px, never wider than the container.
- side
- "bottom" | "top"
- default "bottom"
- Which way the list opens from the trigger.
- color
- string
- default "var(--accent-bright)"
- The car, the wash (mixed at 13% in oklab) and the chosen node. Any CSS colour.
- railColor
- string
- default "var(--hairline-strong)"
- The rail and its nodes. Any CSS colour.
- name
- string
- default —
- Submits the value with a form under this name, through a hidden input.
- size
- "sm" | "md" | "lg"
- default "md"
- Trigger height 32, 36 or 40px and row height 28, 32 or 36px.
- sound
- boolean
- default false
- Play a tick as the highlight moves, pitched by its place on the rail, and a lower one on a choice.
- disabled
- boolean
- default false
- Disables the trigger and keeps the list shut.
- className
- string
- default —
- Classes for the root column (caption, trigger and list).
| Prop | Type | Default | Description |
|---|---|---|---|
| options | { value: string; label: string; hint?: string; disabled?: boolean }[] | — | The stops on the rail, top to bottom; a hint sits at the row's end, and disabled options are skipped by every key. |
| value / defaultValue | string | null | — / null | The chosen option's value, controlled or uncontrolled. |
| onValueChange | (value: string) => void | — | Fires from the click or key that chose an option. |
| open / defaultOpen | boolean | — / false | Whether the list is open, controlled or uncontrolled. |
| onOpenChange | (open: boolean) => void | — | Fires from the press, key or outside press that opened or closed it. |
| label | string | — | What is being chosen: the caption above the trigger and the list's accessible name. |
| hideLabel | boolean | false | Keep the caption for assistive technology only. |
| placeholder | string | "Choose" | The trigger's text before anything is chosen. |
| stretch | number | 0.5 | How far the highlight stretches toward the next option before its tail catches up, 0 to 1: the trailing edge's spring softens and lags as it rises. |
| speed | number | 0.5 | How quick the ride is, 0 to 1: the leading edge's stiffness, from a heavy car to a light one. |
| width | number | 240 | The trigger's and the list's width in px, never wider than the container. |
| side | "bottom" | "top" | "bottom" | Which way the list opens from the trigger. |
| color | string | "var(--accent-bright)" | The car, the wash (mixed at 13% in oklab) and the chosen node. Any CSS colour. |
| railColor | string | "var(--hairline-strong)" | The rail and its nodes. Any CSS colour. |
| name | string | — | Submits the value with a form under this name, through a hidden input. |
| size | "sm" | "md" | "lg" | "md" | Trigger height 32, 36 or 40px and row height 28, 32 or 36px. |
| sound | boolean | false | Play a tick as the highlight moves, pitched by its place on the rail, and a lower one on a choice. |
| disabled | boolean | false | Disables the trigger and keeps the list shut. |
| className | string | — | Classes for the root column (caption, trigger and list). |