KQ-1283 · TAB/STRIP
Tab Strip
A document tab strip whose tabs share one width that narrows as more open; the ones that no longer fit go into an overflow menu carrying their count, which rolls on springs.snap, and the active tab always stays in view. Every tab's slot and width are motion values on springs.glide, so a closing tab collapses while the rest are pulled left with no gap and a new tab grows out of the plus; a dragged tab lifts, leans into its speed and follows the finger 1:1 while its neighbours make room on the reorder spring. The panel cross-fades with a slide toward the side of the tab you moved to and its height glides to the new one; the tablist's arrows move and select, Shift with an arrow moves a tab, Delete closes one, and the overflow is a real menu button.
| Pump | Window | Flow |
|---|---|---|
| North well | 06:00–08:30 | 12 l/s |
| Weir gate | 09:00–11:00 | 8 l/s |
| Basin Road | 13:30–15:00 | 15 l/s |
Pump schedule · 4 open · drag a tab, press + for more
Install
One command — the source lands in your repo. Or copy it from the Code tab.
$ pnpm dlx shadcn@latest add @kinetiq/tab-strip
Props
- tabs / defaultTabs
- TabStripTab[]
- default defaultStripTabs
- The open tabs in order, controlled or initial: id, title, optional icon, content, dirty and pinned. defaultStripTabs is four Fernworks documents.
- onTabsChange
- (tabs: TabStripTab[]) => void
- default —
- Fires with the whole list after a close, a new tab or a reorder.
- value / defaultValue
- string
- default the first tab's id
- The active tab's id, controlled or initial.
- onValueChange
- (id: string) => void
- default —
- Fires from the press, key or menu choice that made a tab active, and when closing the active tab hands over to its neighbour.
- onCreate
- () => TabStripTab | void
- default an empty "Untitled n"
- Makes the tab the plus opens; return nothing to open none.
- onClose
- (id: string) => void
- default —
- A tab was closed, by its button, Delete or the shortcut.
- label
- string
- default "Documents"
- The tablist's accessible name.
- width
- number
- default 168
- How wide a tab is while there is room, in px; past that they all narrow together.
- minWidth
- number
- default 96
- Narrowest a tab gets before the ones that no longer fit go into the overflow menu, in px.
- reorder
- "glide" | "snap" | "off"
- default "glide"
- How the other tabs make room for a dragged one: smoothly, with one crisp overshoot, or not at all (dragging off).
- slide
- number
- default 12
- How far the panel slides as it changes, in px, toward the side of the tab you moved to; 0 cross-fades in place.
- maxTabs
- number
- default 12
- The plus is disabled at this many open tabs.
- closable
- boolean
- default true
- Close buttons on tabs and Delete to close; pinned tabs never close.
- shortcuts
- boolean
- default false
- Ctrl or Cmd with W closes the active tab and with T opens one, where the browser passes those keys on.
- newLabel
- string
- default "New tab"
- The plus button's accessible name.
- size
- "sm" | "md" | "lg"
- default "md"
- Strip height 32, 36 or 40 px.
- accent
- string
- default "var(--accent-bright)"
- The active tab's rule and the unsaved dot; any CSS colour.
- sound
- boolean
- default false
- A click for each tab chosen (pitched by its slot) and each slot a dragged tab takes, a pop as a new tab grows and a lower one as a tab closes.
- disabled
- boolean
- default false
- Disables the tabs, the plus and the menu.
- className
- string
- default —
- Classes for the strip and its panel.
| Prop | Type | Default | Description |
|---|---|---|---|
| tabs / defaultTabs | TabStripTab[] | defaultStripTabs | The open tabs in order, controlled or initial: id, title, optional icon, content, dirty and pinned. defaultStripTabs is four Fernworks documents. |
| onTabsChange | (tabs: TabStripTab[]) => void | — | Fires with the whole list after a close, a new tab or a reorder. |
| value / defaultValue | string | the first tab's id | The active tab's id, controlled or initial. |
| onValueChange | (id: string) => void | — | Fires from the press, key or menu choice that made a tab active, and when closing the active tab hands over to its neighbour. |
| onCreate | () => TabStripTab | void | an empty "Untitled n" | Makes the tab the plus opens; return nothing to open none. |
| onClose | (id: string) => void | — | A tab was closed, by its button, Delete or the shortcut. |
| label | string | "Documents" | The tablist's accessible name. |
| width | number | 168 | How wide a tab is while there is room, in px; past that they all narrow together. |
| minWidth | number | 96 | Narrowest a tab gets before the ones that no longer fit go into the overflow menu, in px. |
| reorder | "glide" | "snap" | "off" | "glide" | How the other tabs make room for a dragged one: smoothly, with one crisp overshoot, or not at all (dragging off). |
| slide | number | 12 | How far the panel slides as it changes, in px, toward the side of the tab you moved to; 0 cross-fades in place. |
| maxTabs | number | 12 | The plus is disabled at this many open tabs. |
| closable | boolean | true | Close buttons on tabs and Delete to close; pinned tabs never close. |
| shortcuts | boolean | false | Ctrl or Cmd with W closes the active tab and with T opens one, where the browser passes those keys on. |
| newLabel | string | "New tab" | The plus button's accessible name. |
| size | "sm" | "md" | "lg" | "md" | Strip height 32, 36 or 40 px. |
| accent | string | "var(--accent-bright)" | The active tab's rule and the unsaved dot; any CSS colour. |
| sound | boolean | false | A click for each tab chosen (pitched by its slot) and each slot a dragged tab takes, a pop as a new tab grows and a lower one as a tab closes. |
| disabled | boolean | false | Disables the tabs, the plus and the menu. |
| className | string | — | Classes for the strip and its panel. |