Morph Tabs
InteractiveAnimated WAI-ARIA tabs whose shared pill/underline indicator springs between triggers while panels cross-fade and slide in the travel direction.
Signal, not noise
A calm snapshot of everything moving through your workspace this week.
Installation
npx shadcn@latest add https://parable-three.vercel.app/r/morph-tabs.jsonRuns the shadcn CLI — the component source is copied intocomponents/parable/morph-tabs.tsx. You own it from there.
Usage
The exact code rendering the preview above.
<div style={{ width: "100%", maxWidth: 440, padding: 28 }}>
<MorphTabs
variant="pill"
defaultValue="overview"
tabs={[
{
value: "overview",
label: "Overview",
icon: <LayoutGrid />,
content: (
<div style={{ padding: "18px 6px 6px" }}>
<p style={{ margin: 0, color: "#fff", fontWeight: 600 }}>Signal, not noise</p>
<p style={{ margin: "6px 0 0" }}>A calm snapshot of everything moving through your workspace this week.</p>
</div>
),
},
{
value: "insights",
label: "Insights",
icon: <Sparkles />,
content: (
<div style={{ padding: "18px 6px 6px" }}>
<p style={{ margin: 0, color: "#fff", fontWeight: 600 }}>Up and to the right</p>
<p style={{ margin: "6px 0 0" }}>Engagement climbed 24% after the last release. The pill glides; panels slide.</p>
</div>
),
},
{
value: "controls",
label: "Controls",
icon: <SlidersHorizontal />,
content: (
<div style={{ padding: "18px 6px 6px" }}>
<p style={{ margin: 0, color: "#fff", fontWeight: 600 }}>Tune the flow</p>
<p style={{ margin: "6px 0 0" }}>Arrow keys move selection, Home/End jump to the ends — focus and selection stay in sync.</p>
</div>
),
},
]}
/>
</div>Props
MorphTabsProps extends Omit<React.HTMLAttributes<HTMLDivElement>, "defaultValue">
| Prop | Type | Default | Description |
|---|---|---|---|
| tabs* | MorphTab[] | — | Tabs to render, in visual order. |
| value | string | — | Controlled selected value. Pair with `onValueChange`. |
| defaultValue | string | — | Uncontrolled initial value. Defaults to the first tab. |
| onValueChange | (value: string) => void | — | Fires with the next value whenever the selection changes. |
| variant | "pill" | "underline" | "pill" | Indicator style: a gliding `pill` behind the label, or an `underline` bar. |
npm dependencies
- motion
- lucide-react