Components
Tab Bar
Floating glass tab bar with a search button: search collapses the tabs into a circle and stretches the button into a field, and the two glass surfaces fuse when pressed.

Tap search: the tabs fold into a circle as the button stretches into a field. Hold the button to see the two surfaces fuse.
Installation
pnpm dlx shadcn@latest add https://liquidcn.snmandela.com/r/liquid-tab-bar.jsonThe command copies these files into your project:
components/ui/liquid/tab-bar.tsxcomponents/ui/liquid/tabs.tsxcomponents/ui/liquid/liquid.csslib/liquid/motion.tslib/liquid/press.tslib/liquid/lens.tslib/liquid/lens-parts.tslib/liquid/fusion.tslib/liquid/bar.tslib/liquid/refraction.tslib/liquid/tab-bar.ts
It also installs lucide-react.
It extends the shadcn tabs component, whichever primitive library yours is built on. If your project doesn't have it yet, the CLI adds it from shadcn's registry; if it does, yours stays as it is, and the CLI asks before replacing it.
Usage
import { TabBar, TabBarItems, TabBarSearch } from "@/components/ui/liquid/tab-bar"
import { Tabs, TabsList, TabsTrigger } from "@/components/ui/liquid/tabs"
<Tabs defaultValue="calls">
<TabBar>
<TabBarItems icon={<Clock />} label="Back to Calls">
<TabsList aria-label="Phone" className="liquid-tabbar">
<TabsTrigger value="calls"><Clock />Calls</TabsTrigger>
<TabsTrigger value="contacts"><CircleUserRound />Contacts</TabsTrigger>
</TabsList>
</TabBarItems>
<TabBarSearch placeholder="Search" value={query} onChange={search} />
</TabBar>
</Tabs>Examples
Text tabs
Any liquid TabsList works inside TabBarItems, like the iPad tab bar. Uncontrolled, the tab bar keeps its own search state.

API reference
TabBar
Lays out the tabs and the search button, and owns the search state.
| Prop | Type | Default |
|---|---|---|
searchingWhether search is open. Leave it undefined to let the tab bar manage it. | boolean | None |
defaultSearchingInitial state when uncontrolled. | boolean | false |
onSearchingChangeCalled when search opens or closes. | (searching: boolean) => void | None |
TabBarItems
The glass around a liquid TabsList (or a list of links with a lens). While searching, the tabs are inert and a circle stands in for them.
| Prop | Type | Default |
|---|---|---|
iconShown in the circle, usually the selected tab's icon. | ReactNode | None |
labelAccessible name of the circle, which closes search. | string | "Show tabs" |
TabBarSearch
The search button that becomes the field. Props, ref, and events go to the <input>; className styles the glass.
| Prop | Type | Default |
|---|---|---|
closeLabelAccessible name of the close button in the field. | string | "Close search" |
--liquid-tab-bar-widthWidth of the whole bar while searching, set on | CSS length | the bar's resting width |
Motion
| Parameter | Value |
|---|---|
| Search morph | Each part's width springs from the old layout to the new one |
| Tabs out | Fade, 6 px blur, and 0.86 scale over 220–360 ms |
| Field in | Search glass pops with a 2.4/s scale impulse; content condenses in 380 ms |
| Fusion | Metaball neck while pressed, spread up to 0.5 of the gap |
| Refraction | Rim bends the backdrop up to 60% of an 18 px bezel (Chromium) |
Springs keep their velocity when interrupted, and every value here is removed under reduced motion. How the motion works
Accessibility
- The search button has an accessible name, and opening search moves focus to the field.
- Escape, the close button, or the circle closes search and returns focus to the search button.
- While searching, the tabs are
inertand hidden from assistive technology, so focus cannot reach what is folded away. - The neck and the refraction are decorative:
aria-hidden, off under reduced motion, reduced transparency, increased contrast, and forced colors.