Stack content and let the surfaces behave like liquid. As the gap between cards shrinks their rounded surfaces bulge and fuse through a gooey metaball bridge; push it negative and the upper cards frost over — heavy blur, fading opacity — and dissolve into the anchor. Toggle collapsed, or drive gap from a slider for the full continuous sweep.
Stacks with more than two children collapse rank by rank — the ones furthest
from the anchor recede and frost the most, with a staggered spring. A notification
tray folds its history into the newest alert:
The same shape reads as a wallet — three cards fanned out, folding into a stack.
Keep children on the fused bg-card surface and let color come from small accents,
so the metaball bridge stays seamless:
pnpm dlx shadcn@latest add "https://myndui.design/r/gooey-stack.json"
Two layers do the work. GooeyStack draws a surface layer — one bg-card
rounded rect per child — and runs it through an SVG goo filter (feGaussianBlur
→ feColorMatrix threshold), so surfaces that come close bulge and fuse into one
continuous shape. Your children render sharp and unfiltered on top, so text
and controls stay crisp while only the surfaces merge and the receding ones frost.
import { GooeyStack } from "@/components/myndui/gooey-stack";
Children are the inner content of each card — GooeyStack supplies the fused
bg-card surface, so give children padding and content, not their own background:
<GooeyStack collapsed={collapsed}>
<div className="px-5 py-4">First</div>
<div className="px-5 py-4">Second</div>
</GooeyStack>
The last child is the anchor — it stays pinned while the others slide down and
merge into it.
Set gap (px) directly for the full continuous effect — positive values space the
cards out, small values neck them together, negative values overlap and frost:
<GooeyStack gap={gap}>{/* content */}</GooeyStack>
gooeyness is the blur radius feeding the filter: raise it to fuse from further,
lower it for a tighter neck. expandedGap sets the resting spacing, collapsedGap
how deep the collapsed toggle merges.
<GooeyStack gooeyness={10} expandedGap={18} collapsedGap={-48} radius={28}>
{/* content */}
</GooeyStack>
The stack is presentational — drive collapsed or gap from your own control (a
button, switch, slider, or route). The surface layer is aria-hidden, so only your
content is read. When prefers-reduced-motion is set, the goo filter is dropped
and the cards snap between states instead of springing.
| Prop | Type | Default | Description |
|---|
children | React.ReactNode | — | Inner content of each card (transparent). The last child is the anchor. |
gap | number | — | Controlled gap (px); negative overlaps. Overrides collapsed. |
collapsed | boolean | false | Convenience toggle between expandedGap and collapsedGap. |
expandedGap | number | 18 | Resting gap (px) when expanded. |
collapsedGap | number | -48 | Gap (px) the collapsed toggle merges to. |
gooeyness | number | 10 | Blur radius feeding the goo filter — larger fuses from further. |
radius | number | 28 | Corner radius (px) of the fused surfaces. |
GooeyStack also forwards every standard <div> attribute to the root element.