KQ-1180 · LIQUID/TABBAR
Liquid Tab Bar
A bottom tab bar whose indicator is a drop of liquid behind the chosen icon: choosing a tab sends the drop's head racing there on a spring that keeps its velocity while the tail holds back on a softer, later spring, and the neck between them is drawn every frame as a metaball bridge that thins as it stretches and pinches off when stretched too far. The head squashes along its speed and lands with a jiggle whose size is set by the viscosity, and the icon it lands on bounces on recoil; the icons are drawn a second time in the tint's contrast colour, clipped to the drop's live shape. The drop can be dragged 1:1 and thrown to a tab, and the tablist's arrows move and choose, Home and End jump.
This week
Groceries 64.20
Coffee 9.80
Activity · 2 of 5 · flowed 0 times
Install
One command — the source lands in your repo. Or copy it from the Code tab.
$ pnpm dlx shadcn@latest add @kinetiq/liquid-tabbar
Props
- items
- { id: string; label: string; icon: "home" | "search" | "inbox" | "chart" | "wallet" | "card" | "bell" | "calendar" | "person" }[]
- default —
- The tabs, in order, each with a label and a built-in glyph.
- value / defaultValue
- string
- default the first item
- The chosen tab's id, controlled or initial.
- onValueChange
- (id: string) => void
- default —
- Fires from the tap, drag or key that chose a tab.
- label
- string
- default "Sections"
- The tablist's accessible name.
- panelId
- string
- default —
- The id of the panel the tabs control, for aria-controls.
- viscosity
- number
- default 0.5
- Runny (0) to thick (1): quick and splashy with a short neck and more jiggle, or slow and composed with a neck that stretches much further before it pinches.
- tabs
- number
- default —
- How many of items the bar holds, 3 to 5. Defaults to all, up to 5.
- tint
- "accent" | "ink" | "signal"
- default "accent"
- The drop's colour and the contrast colour of the icon it covers; accent and signal are fixed pigments, ink follows the theme.
- sound
- boolean
- default false
- A blup when the drop lands on a tab the visitor chose, pitched by the tab and the viscosity.
- disabled
- boolean
- default false
- The tabs and the drag cannot be used.
- className
- string
- default —
- Classes for the root, which is w-full up to max-w-md.
| Prop | Type | Default | Description |
|---|---|---|---|
| items | { id: string; label: string; icon: "home" | "search" | "inbox" | "chart" | "wallet" | "card" | "bell" | "calendar" | "person" }[] | — | The tabs, in order, each with a label and a built-in glyph. |
| value / defaultValue | string | the first item | The chosen tab's id, controlled or initial. |
| onValueChange | (id: string) => void | — | Fires from the tap, drag or key that chose a tab. |
| label | string | "Sections" | The tablist's accessible name. |
| panelId | string | — | The id of the panel the tabs control, for aria-controls. |
| viscosity | number | 0.5 | Runny (0) to thick (1): quick and splashy with a short neck and more jiggle, or slow and composed with a neck that stretches much further before it pinches. |
| tabs | number | — | How many of items the bar holds, 3 to 5. Defaults to all, up to 5. |
| tint | "accent" | "ink" | "signal" | "accent" | The drop's colour and the contrast colour of the icon it covers; accent and signal are fixed pigments, ink follows the theme. |
| sound | boolean | false | A blup when the drop lands on a tab the visitor chose, pitched by the tab and the viscosity. |
| disabled | boolean | false | The tabs and the drag cannot be used. |
| className | string | — | Classes for the root, which is w-full up to max-w-md. |