32 / 36
All 36 in 36 Tailwind Stacked Cards
- 01Tailwind Scroll-Driven Scaling Card Stack
- 02Tailwind Swipe Card Deck Drag to Dismiss
- 03Tailwind Glassmorphism Testimonial Card Stack
- 04Tailwind Stacked Cards Expand Accordion on Click
- 05Tailwind Fan Out Photo Stack on Hover
- 06Tailwind 3D Perspective Tilt Card Stack
- 07Tailwind Multi Step Form Card Stack
- 08Tailwind Tabbed Card Stack Bring to Front
- 09Tailwind Click to Front Stacked Cards Cycle
- 10Tailwind Classic Card Deck Stack Shadow
- 11Tailwind Fan Spread Hand of Cards
- 12Tailwind Polaroid Photo Stack Scattered Rotate
- 13Tailwind Staircase Offset Stacked Cards Cascade
- 14Tailwind Cards Spread Out Horizontally on Hover
- 15Tailwind Stacked Cards Lift on Hover Sibling Push
- 16Tailwind Accordion Stacked Cards Slide Out Menu
- 17Tailwind Shuffle Cards Animation Loop Deck
- 18Tailwind File Folder Tab Stacked Cards
- 19Tailwind Scattered Photos Pinch to Centre on Hover
- 20Tailwind Mobile Swipe Card Stack Translate3d
- 21Tailwind Single Accordion Card Nested Layers
- 22Tailwind Isometric Tower Stacked Cards 3D Column
- 23Tailwind 3D Flip Card Stack Reveal Back
- 24Tailwind Card Stack Tilt on Hover Quadrant
- 25Tailwind Notification Stack Pile Dismiss
- 26Tailwind Receipt Roll Unroll Stacked Items
- 27Tailwind Scratch Card Reveal Canvas Mask
- 28Tailwind Stacked Cards Hover Reveal Spread
- 29Tailwind Scroll Activated Sticky Stacking Cards
- 30Tailwind Stacked Testimonial Slider Radio
- 31Tailwind Isometric Overlapping Stacked Cards
- 32Tailwind Bento Grid Stacked Cards Hover Fan
- 33Tailwind Perspective Card Deck Depth Tunnel
- 34Tailwind Peel Back Corner Card Reveal
- 35Tailwind Timeline Stacked Milestone Cards
- 36Tailwind Spiral Tower Rotating Stacked Cards
Tailwind Bento Grid Stacked Cards Hover Fan
A dashboard bento where one tile hides a stacked mini-deck: three channel breakdowns collapsed into a single cell that fans open on hover. This is the demo where Tailwind's grid utilities do the heavy lifting for free — col-span-2, row-span-2 and a fixed track list replace named grid areas entirely — while the fan itself is the index pattern one more time.
Untitled UI Pro: 5,000+ React + Tailwind CSS v4 components · from $349 one-time
Untitled UI React · Affiliate link
The code
<section class="stk-32 grid w-full min-h-svh place-items-center p-5 font-stk32-sans bg-stk32-bg" aria-label="Analytics dashboard preview">
<div class="grid grid-cols-[repeat(3,118px)] grid-rows-[repeat(3,118px)] gap-3">
<div class="group relative z-5 row-span-2 overflow-visible rounded-[18px] border border-stk32-line bg-stk32-tile p-4 transition-colors duration-300 hover:z-20 hover:border-stk32-lineh">
<span class="text-[0.68rem] tracking-[0.12em] uppercase text-stk32-lbl">Channels</span>
<div class="absolute inset-x-4 bottom-3.5 h-13.5">
<div class="pointer-events-none absolute inset-x-0 h-13.5 rounded-xl bg-stk32-m1 px-3 py-2 text-[0.72rem] text-white shadow-[0_8px_18px_rgba(0,0,0,0.55)] [z-index:calc(3-var(--i))] transition-transform duration-450 ease-stk32 group-hover:[transform:translateY(calc(var(--i)*-58px))] motion-reduce:transition-none" style="--i:0">
<span class="text-[0.68rem] tracking-[0.12em] uppercase text-white/85">Organic</span>
<b class="mt-0.5 block text-[0.95rem]">62%</b>
</div>
<div class="pointer-events-none absolute inset-x-0 h-13.5 rounded-xl bg-stk32-m2 px-3 py-2 text-[0.72rem] text-white shadow-[0_8px_18px_rgba(0,0,0,0.55)] [z-index:calc(3-var(--i))] transition-transform duration-450 ease-stk32 group-hover:[transform:translateY(calc(var(--i)*-58px))] motion-reduce:transition-none" style="--i:1">
<span class="text-[0.68rem] tracking-[0.12em] uppercase text-white/85">Paid</span>
<b class="mt-0.5 block text-[0.95rem]">24%</b>
</div>
<div class="pointer-events-none absolute inset-x-0 h-13.5 rounded-xl bg-stk32-m3 px-3 py-2 text-[0.72rem] text-white shadow-[0_8px_18px_rgba(0,0,0,0.55)] [z-index:calc(3-var(--i))] transition-transform duration-450 ease-stk32 group-hover:[transform:translateY(calc(var(--i)*-58px))] motion-reduce:transition-none" style="--i:2">
<span class="text-[0.68rem] tracking-[0.12em] uppercase text-white/85">Referral</span>
<b class="mt-0.5 block text-[0.95rem]">14%</b>
</div>
</div>
</div>
<div class="relative col-span-2 overflow-hidden rounded-[18px] border border-stk32-line bg-stk32-tile p-4 transition-colors duration-300 hover:border-stk32-lineh">
<div class="pointer-events-none absolute -right-8 -top-8 size-24 rounded-full bg-stk32-good opacity-20 blur-2xl" aria-hidden="true"></div>
<span class="text-[0.68rem] tracking-[0.12em] uppercase text-stk32-lbl">Revenue</span>
<div class="mt-1 text-[1.6rem] font-bold text-stk32-val">$48.2k <small class="text-[0.8rem] font-semibold text-stk32-good">+12%</small></div>
<div class="mt-2 flex h-8 items-end gap-1.5" aria-hidden="true">
<span class="w-2 rounded-t bg-stk32-bar" style="height:40%"></span>
<span class="w-2 rounded-t bg-stk32-bar" style="height:55%"></span>
<span class="w-2 rounded-t bg-stk32-bar" style="height:45%"></span>
<span class="w-2 rounded-t bg-stk32-bar" style="height:70%"></span>
<span class="w-2 rounded-t bg-stk32-bar" style="height:60%"></span>
<span class="w-2 rounded-t bg-stk32-bar" style="height:85%"></span>
<span class="w-2 rounded-t bg-stk32-good" style="height:100%"></span>
</div>
</div>
<div class="relative overflow-hidden rounded-[18px] border border-stk32-line bg-stk32-tile p-4 transition-colors duration-300 hover:border-stk32-lineh">
<span class="text-[0.68rem] tracking-[0.12em] uppercase text-stk32-lbl">Users</span>
<div class="mt-1 text-[1.6rem] font-bold text-stk32-val">9.1k</div>
</div>
<div class="relative overflow-hidden rounded-[18px] border border-stk32-line bg-stk32-tile p-4 transition-colors duration-300 hover:border-stk32-lineh">
<span class="text-[0.68rem] tracking-[0.12em] uppercase text-stk32-lbl">Churn</span>
<div class="mt-1 text-[1.6rem] font-bold text-stk32-bad">1.8%</div>
</div>
<div class="relative overflow-hidden rounded-[18px] border border-stk32-line bg-stk32-tile p-4 transition-colors duration-300 hover:border-stk32-lineh">
<div class="pointer-events-none absolute -left-8 -bottom-8 size-24 rounded-full bg-stk32-m1 opacity-25 blur-2xl" aria-hidden="true"></div>
<span class="text-[0.68rem] tracking-[0.12em] uppercase text-stk32-lbl">NPS</span>
<div class="mt-1 text-[1.6rem] font-bold text-stk32-val">72</div>
</div>
</div>
</section><section class="stk-32 grid w-full min-h-svh place-items-center p-5 font-stk32-sans bg-stk32-bg" aria-label="Analytics dashboard preview">
<div class="grid grid-cols-[repeat(3,118px)] grid-rows-[repeat(3,118px)] gap-3">
<div class="group relative z-5 row-span-2 overflow-visible rounded-[18px] border border-stk32-line bg-stk32-tile p-4 transition-colors duration-300 hover:z-20 hover:border-stk32-lineh">
<span class="text-[0.68rem] tracking-[0.12em] uppercase text-stk32-lbl">Channels</span>
<div class="absolute inset-x-4 bottom-3.5 h-13.5">
<div class="pointer-events-none absolute inset-x-0 h-13.5 rounded-xl bg-stk32-m1 px-3 py-2 text-[0.72rem] text-white shadow-[0_8px_18px_rgba(0,0,0,0.55)] [z-index:calc(3-var(--i))] transition-transform duration-450 ease-stk32 group-hover:[transform:translateY(calc(var(--i)*-58px))] motion-reduce:transition-none" style="--i:0">
<span class="text-[0.68rem] tracking-[0.12em] uppercase text-white/85">Organic</span>
<b class="mt-0.5 block text-[0.95rem]">62%</b>
</div>
<div class="pointer-events-none absolute inset-x-0 h-13.5 rounded-xl bg-stk32-m2 px-3 py-2 text-[0.72rem] text-white shadow-[0_8px_18px_rgba(0,0,0,0.55)] [z-index:calc(3-var(--i))] transition-transform duration-450 ease-stk32 group-hover:[transform:translateY(calc(var(--i)*-58px))] motion-reduce:transition-none" style="--i:1">
<span class="text-[0.68rem] tracking-[0.12em] uppercase text-white/85">Paid</span>
<b class="mt-0.5 block text-[0.95rem]">24%</b>
</div>
<div class="pointer-events-none absolute inset-x-0 h-13.5 rounded-xl bg-stk32-m3 px-3 py-2 text-[0.72rem] text-white shadow-[0_8px_18px_rgba(0,0,0,0.55)] [z-index:calc(3-var(--i))] transition-transform duration-450 ease-stk32 group-hover:[transform:translateY(calc(var(--i)*-58px))] motion-reduce:transition-none" style="--i:2">
<span class="text-[0.68rem] tracking-[0.12em] uppercase text-white/85">Referral</span>
<b class="mt-0.5 block text-[0.95rem]">14%</b>
</div>
</div>
</div>
<div class="relative col-span-2 overflow-hidden rounded-[18px] border border-stk32-line bg-stk32-tile p-4 transition-colors duration-300 hover:border-stk32-lineh">
<div class="pointer-events-none absolute -right-8 -top-8 size-24 rounded-full bg-stk32-good opacity-20 blur-2xl" aria-hidden="true"></div>
<span class="text-[0.68rem] tracking-[0.12em] uppercase text-stk32-lbl">Revenue</span>
<div class="mt-1 text-[1.6rem] font-bold text-stk32-val">$48.2k <small class="text-[0.8rem] font-semibold text-stk32-good">+12%</small></div>
<div class="mt-2 flex h-8 items-end gap-1.5" aria-hidden="true">
<span class="w-2 rounded-t bg-stk32-bar" style="height:40%"></span>
<span class="w-2 rounded-t bg-stk32-bar" style="height:55%"></span>
<span class="w-2 rounded-t bg-stk32-bar" style="height:45%"></span>
<span class="w-2 rounded-t bg-stk32-bar" style="height:70%"></span>
<span class="w-2 rounded-t bg-stk32-bar" style="height:60%"></span>
<span class="w-2 rounded-t bg-stk32-bar" style="height:85%"></span>
<span class="w-2 rounded-t bg-stk32-good" style="height:100%"></span>
</div>
</div>
<div class="relative overflow-hidden rounded-[18px] border border-stk32-line bg-stk32-tile p-4 transition-colors duration-300 hover:border-stk32-lineh">
<span class="text-[0.68rem] tracking-[0.12em] uppercase text-stk32-lbl">Users</span>
<div class="mt-1 text-[1.6rem] font-bold text-stk32-val">9.1k</div>
</div>
<div class="relative overflow-hidden rounded-[18px] border border-stk32-line bg-stk32-tile p-4 transition-colors duration-300 hover:border-stk32-lineh">
<span class="text-[0.68rem] tracking-[0.12em] uppercase text-stk32-lbl">Churn</span>
<div class="mt-1 text-[1.6rem] font-bold text-stk32-bad">1.8%</div>
</div>
<div class="relative overflow-hidden rounded-[18px] border border-stk32-line bg-stk32-tile p-4 transition-colors duration-300 hover:border-stk32-lineh">
<div class="pointer-events-none absolute -left-8 -bottom-8 size-24 rounded-full bg-stk32-m1 opacity-25 blur-2xl" aria-hidden="true"></div>
<span class="text-[0.68rem] tracking-[0.12em] uppercase text-stk32-lbl">NPS</span>
<div class="mt-1 text-[1.6rem] font-bold text-stk32-val">72</div>
</div>
</div>
</section>@import "tailwindcss";
@theme {
--color-stk32-bg: #0e0f13;
--color-stk32-bg: oklch(0.17 0.008 275);
--color-stk32-tile: #191b22;
--color-stk32-tile: oklch(0.23 0.012 275);
--color-stk32-line: #262934;
--color-stk32-line: oklch(0.30 0.014 275);
--color-stk32-lineh: #3a4050;
--color-stk32-lineh: oklch(0.40 0.018 275);
--color-stk32-lbl: #8a90a0;
--color-stk32-lbl: oklch(0.64 0.016 275);
--color-stk32-val: #f3f4f6;
--color-stk32-val: oklch(0.97 0.003 275);
--color-stk32-bar: #4b5162;
--color-stk32-bar: oklch(0.46 0.020 275);
--color-stk32-good: #34d399;
--color-stk32-good: oklch(0.78 0.15 165);
--color-stk32-bad: #fca5a5;
--color-stk32-bad: oklch(0.79 0.10 20);
--color-stk32-m1: #818cf8;
--color-stk32-m1: oklch(0.68 0.15 280);
--color-stk32-m2: #f472b6;
--color-stk32-m2: oklch(0.72 0.17 350);
--color-stk32-m3: #2dd4bf;
--color-stk32-m3: oklch(0.79 0.13 180);
--font-stk32-sans: 'Geist', system-ui, sans-serif;
--ease-stk32: cubic-bezier(0.3, 0.9, 0.3, 1);
}@import "tailwindcss";
@theme {
--color-stk32-bg: #0e0f13;
--color-stk32-bg: oklch(0.17 0.008 275);
--color-stk32-tile: #191b22;
--color-stk32-tile: oklch(0.23 0.012 275);
--color-stk32-line: #262934;
--color-stk32-line: oklch(0.30 0.014 275);
--color-stk32-lineh: #3a4050;
--color-stk32-lineh: oklch(0.40 0.018 275);
--color-stk32-lbl: #8a90a0;
--color-stk32-lbl: oklch(0.64 0.016 275);
--color-stk32-val: #f3f4f6;
--color-stk32-val: oklch(0.97 0.003 275);
--color-stk32-bar: #4b5162;
--color-stk32-bar: oklch(0.46 0.020 275);
--color-stk32-good: #34d399;
--color-stk32-good: oklch(0.78 0.15 165);
--color-stk32-bad: #fca5a5;
--color-stk32-bad: oklch(0.79 0.10 20);
--color-stk32-m1: #818cf8;
--color-stk32-m1: oklch(0.68 0.15 280);
--color-stk32-m2: #f472b6;
--color-stk32-m2: oklch(0.72 0.17 350);
--color-stk32-m3: #2dd4bf;
--color-stk32-m3: oklch(0.79 0.13 180);
--font-stk32-sans: 'Geist', system-ui, sans-serif;
--ease-stk32: cubic-bezier(0.3, 0.9, 0.3, 1);
}Setup — do this once per project
Skip this if your project already runs Tailwind v4.
<script src="https://cdn.jsdelivr.net/npm/@tailwindcss/browser@4"></script>
<script src="https://cdn.jsdelivr.net/npm/@tailwindcss/browser@4"></script>Compiles in the browser — perfect for a CodePen or a quick test, not for production.
npm install tailwindcss @tailwindcss/vite
npm install tailwindcss @tailwindcss/vite// vite.config.js ("type": "module" in package.json)
import { defineConfig } from 'vite'
import tailwindcss from '@tailwindcss/vite'
export default defineConfig({
plugins: [tailwindcss()],
})// vite.config.js ("type": "module" in package.json)
import { defineConfig } from 'vite'
import tailwindcss from '@tailwindcss/vite'
export default defineConfig({
plugins: [tailwindcss()],
})Any v4 setup works — Vite, PostCSS or the CLI. Nothing in this demo is bundler-specific.
@import "tailwindcss";
@theme {
--color-stk32-bg: #0e0f13;
--color-stk32-bg: oklch(0.17 0.008 275);
--color-stk32-tile: #191b22;
--color-stk32-tile: oklch(0.23 0.012 275);
--color-stk32-line: #262934;
--color-stk32-line: oklch(0.30 0.014 275);
--color-stk32-lineh: #3a4050;
--color-stk32-lineh: oklch(0.40 0.018 275);
--color-stk32-lbl: #8a90a0;
--color-stk32-lbl: oklch(0.64 0.016 275);
--color-stk32-val: #f3f4f6;
--color-stk32-val: oklch(0.97 0.003 275);
--color-stk32-bar: #4b5162;
--color-stk32-bar: oklch(0.46 0.020 275);
--color-stk32-good: #34d399;
--color-stk32-good: oklch(0.78 0.15 165);
--color-stk32-bad: #fca5a5;
--color-stk32-bad: oklch(0.79 0.10 20);
--color-stk32-m1: #818cf8;
--color-stk32-m1: oklch(0.68 0.15 280);
--color-stk32-m2: #f472b6;
--color-stk32-m2: oklch(0.72 0.17 350);
--color-stk32-m3: #2dd4bf;
--color-stk32-m3: oklch(0.79 0.13 180);
--font-stk32-sans: 'Geist', system-ui, sans-serif;
--ease-stk32: cubic-bezier(0.3, 0.9, 0.3, 1);
}@import "tailwindcss";
@theme {
--color-stk32-bg: #0e0f13;
--color-stk32-bg: oklch(0.17 0.008 275);
--color-stk32-tile: #191b22;
--color-stk32-tile: oklch(0.23 0.012 275);
--color-stk32-line: #262934;
--color-stk32-line: oklch(0.30 0.014 275);
--color-stk32-lineh: #3a4050;
--color-stk32-lineh: oklch(0.40 0.018 275);
--color-stk32-lbl: #8a90a0;
--color-stk32-lbl: oklch(0.64 0.016 275);
--color-stk32-val: #f3f4f6;
--color-stk32-val: oklch(0.97 0.003 275);
--color-stk32-bar: #4b5162;
--color-stk32-bar: oklch(0.46 0.020 275);
--color-stk32-good: #34d399;
--color-stk32-good: oklch(0.78 0.15 165);
--color-stk32-bad: #fca5a5;
--color-stk32-bad: oklch(0.79 0.10 20);
--color-stk32-m1: #818cf8;
--color-stk32-m1: oklch(0.68 0.15 280);
--color-stk32-m2: #f472b6;
--color-stk32-m2: oklch(0.72 0.17 350);
--color-stk32-m3: #2dd4bf;
--color-stk32-m3: oklch(0.79 0.13 180);
--font-stk32-sans: 'Geist', system-ui, sans-serif;
--ease-stk32: cubic-bezier(0.3, 0.9, 0.3, 1);
}<link rel="preconnect" href="https://fonts.googleapis.com"> <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin> <link rel="stylesheet" href="https://fonts.googleapis.com/css2?family=Geist:wght@400;500;600;700&display=swap">
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link rel="stylesheet" href="https://fonts.googleapis.com/css2?family=Geist:wght@400;500;600;700&display=swap">What changed in the port
Which parts became utilities, which stayed CSS, and why.
- The webfonts load as
<link>tags insetup, not as an@importinside the stylesheet. Tailwind emits user@importrules after its own@layerblocks, and a late@importis invalid CSS — the browser drops it and the demo silently falls back to system fonts. - Named grid areas disappear: an explicit track list plus
col-span-2androw-span-2says the same thing with no naming layer. Areas earn their keep only when the layout changes shape across breakpoints. - The one tile that must escape its cell declares it on itself —
overflow-visibleplushover:z-20— while every other tile stays clipped. - The nested fan is the same index pattern used in demos 05, 13, 22 and 32: one custom property, one multiplied transform.
pointer-events-noneon the mini-cards is load-bearing; without it they intercept the hover meant for the tile fanning them.- The sparkline is real elements with inline heights rather than an SVG, and the whole group is
aria-hiddenbecause the number beside it is the actual content. - Accent glows are blurred circles rather than gradient overlays, which keeps each one a single element and removes the CSS version's inline
style="background:…".
Here's a working CSS demo from CodeFronts. Use it as-is or adapt to your framework. All classes are scoped under a unique prefix so the code won't collide with your existing styles. MIT licensed.
Demo: Tailwind Bento Grid Stacked Cards Hover Fan
Source: https://codefronts.com/components/tailwind-stacked-cards/tailwind-bento-grid-stacked-cards-hover-fan/
A dashboard bento where one tile hides a stacked mini-deck: three channel breakdowns collapsed into a single cell that fans open on hover. This is the demo where Tailwind's grid utilities do the heavy lifting for free — col-span-2, row-span-2 and a fixed track list replace named grid areas entirely — while the fan itself is the index pattern one more time.
## HTML
```html
<section class="stk-32 grid w-full min-h-svh place-items-center p-5 font-stk32-sans bg-stk32-bg" aria-label="Analytics dashboard preview">
<div class="grid grid-cols-[repeat(3,118px)] grid-rows-[repeat(3,118px)] gap-3">
<div class="group relative z-5 row-span-2 overflow-visible rounded-[18px] border border-stk32-line bg-stk32-tile p-4 transition-colors duration-300 hover:z-20 hover:border-stk32-lineh">
<span class="text-[0.68rem] tracking-[0.12em] uppercase text-stk32-lbl">Channels</span>
<div class="absolute inset-x-4 bottom-3.5 h-13.5">
<div class="pointer-events-none absolute inset-x-0 h-13.5 rounded-xl bg-stk32-m1 px-3 py-2 text-[0.72rem] text-white shadow-[0_8px_18px_rgba(0,0,0,0.55)] [z-index:calc(3-var(--i))] transition-transform duration-450 ease-stk32 group-hover:[transform:translateY(calc(var(--i)*-58px))] motion-reduce:transition-none" style="--i:0">
<span class="text-[0.68rem] tracking-[0.12em] uppercase text-white/85">Organic</span>
<b class="mt-0.5 block text-[0.95rem]">62%</b>
</div>
<div class="pointer-events-none absolute inset-x-0 h-13.5 rounded-xl bg-stk32-m2 px-3 py-2 text-[0.72rem] text-white shadow-[0_8px_18px_rgba(0,0,0,0.55)] [z-index:calc(3-var(--i))] transition-transform duration-450 ease-stk32 group-hover:[transform:translateY(calc(var(--i)*-58px))] motion-reduce:transition-none" style="--i:1">
<span class="text-[0.68rem] tracking-[0.12em] uppercase text-white/85">Paid</span>
<b class="mt-0.5 block text-[0.95rem]">24%</b>
</div>
<div class="pointer-events-none absolute inset-x-0 h-13.5 rounded-xl bg-stk32-m3 px-3 py-2 text-[0.72rem] text-white shadow-[0_8px_18px_rgba(0,0,0,0.55)] [z-index:calc(3-var(--i))] transition-transform duration-450 ease-stk32 group-hover:[transform:translateY(calc(var(--i)*-58px))] motion-reduce:transition-none" style="--i:2">
<span class="text-[0.68rem] tracking-[0.12em] uppercase text-white/85">Referral</span>
<b class="mt-0.5 block text-[0.95rem]">14%</b>
</div>
</div>
</div>
<div class="relative col-span-2 overflow-hidden rounded-[18px] border border-stk32-line bg-stk32-tile p-4 transition-colors duration-300 hover:border-stk32-lineh">
<div class="pointer-events-none absolute -right-8 -top-8 size-24 rounded-full bg-stk32-good opacity-20 blur-2xl" aria-hidden="true"></div>
<span class="text-[0.68rem] tracking-[0.12em] uppercase text-stk32-lbl">Revenue</span>
<div class="mt-1 text-[1.6rem] font-bold text-stk32-val">$48.2k <small class="text-[0.8rem] font-semibold text-stk32-good">+12%</small></div>
<div class="mt-2 flex h-8 items-end gap-1.5" aria-hidden="true">
<span class="w-2 rounded-t bg-stk32-bar" style="height:40%"></span>
<span class="w-2 rounded-t bg-stk32-bar" style="height:55%"></span>
<span class="w-2 rounded-t bg-stk32-bar" style="height:45%"></span>
<span class="w-2 rounded-t bg-stk32-bar" style="height:70%"></span>
<span class="w-2 rounded-t bg-stk32-bar" style="height:60%"></span>
<span class="w-2 rounded-t bg-stk32-bar" style="height:85%"></span>
<span class="w-2 rounded-t bg-stk32-good" style="height:100%"></span>
</div>
</div>
<div class="relative overflow-hidden rounded-[18px] border border-stk32-line bg-stk32-tile p-4 transition-colors duration-300 hover:border-stk32-lineh">
<span class="text-[0.68rem] tracking-[0.12em] uppercase text-stk32-lbl">Users</span>
<div class="mt-1 text-[1.6rem] font-bold text-stk32-val">9.1k</div>
</div>
<div class="relative overflow-hidden rounded-[18px] border border-stk32-line bg-stk32-tile p-4 transition-colors duration-300 hover:border-stk32-lineh">
<span class="text-[0.68rem] tracking-[0.12em] uppercase text-stk32-lbl">Churn</span>
<div class="mt-1 text-[1.6rem] font-bold text-stk32-bad">1.8%</div>
</div>
<div class="relative overflow-hidden rounded-[18px] border border-stk32-line bg-stk32-tile p-4 transition-colors duration-300 hover:border-stk32-lineh">
<div class="pointer-events-none absolute -left-8 -bottom-8 size-24 rounded-full bg-stk32-m1 opacity-25 blur-2xl" aria-hidden="true"></div>
<span class="text-[0.68rem] tracking-[0.12em] uppercase text-stk32-lbl">NPS</span>
<div class="mt-1 text-[1.6rem] font-bold text-stk32-val">72</div>
</div>
</div>
</section>
```
## CSS
```css
@import "tailwindcss";
@theme {
--color-stk32-bg: #0e0f13;
--color-stk32-bg: oklch(0.17 0.008 275);
--color-stk32-tile: #191b22;
--color-stk32-tile: oklch(0.23 0.012 275);
--color-stk32-line: #262934;
--color-stk32-line: oklch(0.30 0.014 275);
--color-stk32-lineh: #3a4050;
--color-stk32-lineh: oklch(0.40 0.018 275);
--color-stk32-lbl: #8a90a0;
--color-stk32-lbl: oklch(0.64 0.016 275);
--color-stk32-val: #f3f4f6;
--color-stk32-val: oklch(0.97 0.003 275);
--color-stk32-bar: #4b5162;
--color-stk32-bar: oklch(0.46 0.020 275);
--color-stk32-good: #34d399;
--color-stk32-good: oklch(0.78 0.15 165);
--color-stk32-bad: #fca5a5;
--color-stk32-bad: oklch(0.79 0.10 20);
--color-stk32-m1: #818cf8;
--color-stk32-m1: oklch(0.68 0.15 280);
--color-stk32-m2: #f472b6;
--color-stk32-m2: oklch(0.72 0.17 350);
--color-stk32-m3: #2dd4bf;
--color-stk32-m3: oklch(0.79 0.13 180);
--font-stk32-sans: 'Geist', system-ui, sans-serif;
--ease-stk32: cubic-bezier(0.3, 0.9, 0.3, 1);
}
```Here's a working CSS demo from CodeFronts. Use it as-is or adapt to your framework. All classes are scoped under a unique prefix so the code won't collide with your existing styles. MIT licensed.
Demo: Tailwind Bento Grid Stacked Cards Hover Fan
Source: https://codefronts.com/components/tailwind-stacked-cards/tailwind-bento-grid-stacked-cards-hover-fan/
A dashboard bento where one tile hides a stacked mini-deck: three channel breakdowns collapsed into a single cell that fans open on hover. This is the demo where Tailwind's grid utilities do the heavy lifting for free — col-span-2, row-span-2 and a fixed track list replace named grid areas entirely — while the fan itself is the index pattern one more time.
## HTML
```html
<section class="stk-32 grid w-full min-h-svh place-items-center p-5 font-stk32-sans bg-stk32-bg" aria-label="Analytics dashboard preview">
<div class="grid grid-cols-[repeat(3,118px)] grid-rows-[repeat(3,118px)] gap-3">
<div class="group relative z-5 row-span-2 overflow-visible rounded-[18px] border border-stk32-line bg-stk32-tile p-4 transition-colors duration-300 hover:z-20 hover:border-stk32-lineh">
<span class="text-[0.68rem] tracking-[0.12em] uppercase text-stk32-lbl">Channels</span>
<div class="absolute inset-x-4 bottom-3.5 h-13.5">
<div class="pointer-events-none absolute inset-x-0 h-13.5 rounded-xl bg-stk32-m1 px-3 py-2 text-[0.72rem] text-white shadow-[0_8px_18px_rgba(0,0,0,0.55)] [z-index:calc(3-var(--i))] transition-transform duration-450 ease-stk32 group-hover:[transform:translateY(calc(var(--i)*-58px))] motion-reduce:transition-none" style="--i:0">
<span class="text-[0.68rem] tracking-[0.12em] uppercase text-white/85">Organic</span>
<b class="mt-0.5 block text-[0.95rem]">62%</b>
</div>
<div class="pointer-events-none absolute inset-x-0 h-13.5 rounded-xl bg-stk32-m2 px-3 py-2 text-[0.72rem] text-white shadow-[0_8px_18px_rgba(0,0,0,0.55)] [z-index:calc(3-var(--i))] transition-transform duration-450 ease-stk32 group-hover:[transform:translateY(calc(var(--i)*-58px))] motion-reduce:transition-none" style="--i:1">
<span class="text-[0.68rem] tracking-[0.12em] uppercase text-white/85">Paid</span>
<b class="mt-0.5 block text-[0.95rem]">24%</b>
</div>
<div class="pointer-events-none absolute inset-x-0 h-13.5 rounded-xl bg-stk32-m3 px-3 py-2 text-[0.72rem] text-white shadow-[0_8px_18px_rgba(0,0,0,0.55)] [z-index:calc(3-var(--i))] transition-transform duration-450 ease-stk32 group-hover:[transform:translateY(calc(var(--i)*-58px))] motion-reduce:transition-none" style="--i:2">
<span class="text-[0.68rem] tracking-[0.12em] uppercase text-white/85">Referral</span>
<b class="mt-0.5 block text-[0.95rem]">14%</b>
</div>
</div>
</div>
<div class="relative col-span-2 overflow-hidden rounded-[18px] border border-stk32-line bg-stk32-tile p-4 transition-colors duration-300 hover:border-stk32-lineh">
<div class="pointer-events-none absolute -right-8 -top-8 size-24 rounded-full bg-stk32-good opacity-20 blur-2xl" aria-hidden="true"></div>
<span class="text-[0.68rem] tracking-[0.12em] uppercase text-stk32-lbl">Revenue</span>
<div class="mt-1 text-[1.6rem] font-bold text-stk32-val">$48.2k <small class="text-[0.8rem] font-semibold text-stk32-good">+12%</small></div>
<div class="mt-2 flex h-8 items-end gap-1.5" aria-hidden="true">
<span class="w-2 rounded-t bg-stk32-bar" style="height:40%"></span>
<span class="w-2 rounded-t bg-stk32-bar" style="height:55%"></span>
<span class="w-2 rounded-t bg-stk32-bar" style="height:45%"></span>
<span class="w-2 rounded-t bg-stk32-bar" style="height:70%"></span>
<span class="w-2 rounded-t bg-stk32-bar" style="height:60%"></span>
<span class="w-2 rounded-t bg-stk32-bar" style="height:85%"></span>
<span class="w-2 rounded-t bg-stk32-good" style="height:100%"></span>
</div>
</div>
<div class="relative overflow-hidden rounded-[18px] border border-stk32-line bg-stk32-tile p-4 transition-colors duration-300 hover:border-stk32-lineh">
<span class="text-[0.68rem] tracking-[0.12em] uppercase text-stk32-lbl">Users</span>
<div class="mt-1 text-[1.6rem] font-bold text-stk32-val">9.1k</div>
</div>
<div class="relative overflow-hidden rounded-[18px] border border-stk32-line bg-stk32-tile p-4 transition-colors duration-300 hover:border-stk32-lineh">
<span class="text-[0.68rem] tracking-[0.12em] uppercase text-stk32-lbl">Churn</span>
<div class="mt-1 text-[1.6rem] font-bold text-stk32-bad">1.8%</div>
</div>
<div class="relative overflow-hidden rounded-[18px] border border-stk32-line bg-stk32-tile p-4 transition-colors duration-300 hover:border-stk32-lineh">
<div class="pointer-events-none absolute -left-8 -bottom-8 size-24 rounded-full bg-stk32-m1 opacity-25 blur-2xl" aria-hidden="true"></div>
<span class="text-[0.68rem] tracking-[0.12em] uppercase text-stk32-lbl">NPS</span>
<div class="mt-1 text-[1.6rem] font-bold text-stk32-val">72</div>
</div>
</div>
</section>
```
## CSS
```css
@import "tailwindcss";
@theme {
--color-stk32-bg: #0e0f13;
--color-stk32-bg: oklch(0.17 0.008 275);
--color-stk32-tile: #191b22;
--color-stk32-tile: oklch(0.23 0.012 275);
--color-stk32-line: #262934;
--color-stk32-line: oklch(0.30 0.014 275);
--color-stk32-lineh: #3a4050;
--color-stk32-lineh: oklch(0.40 0.018 275);
--color-stk32-lbl: #8a90a0;
--color-stk32-lbl: oklch(0.64 0.016 275);
--color-stk32-val: #f3f4f6;
--color-stk32-val: oklch(0.97 0.003 275);
--color-stk32-bar: #4b5162;
--color-stk32-bar: oklch(0.46 0.020 275);
--color-stk32-good: #34d399;
--color-stk32-good: oklch(0.78 0.15 165);
--color-stk32-bad: #fca5a5;
--color-stk32-bad: oklch(0.79 0.10 20);
--color-stk32-m1: #818cf8;
--color-stk32-m1: oklch(0.68 0.15 280);
--color-stk32-m2: #f472b6;
--color-stk32-m2: oklch(0.72 0.17 350);
--color-stk32-m3: #2dd4bf;
--color-stk32-m3: oklch(0.79 0.13 180);
--font-stk32-sans: 'Geist', system-ui, sans-serif;
--ease-stk32: cubic-bezier(0.3, 0.9, 0.3, 1);
}
```How this works
Named areas are unnecessary here, and that is a real finding. A fixed 3×3 bento is fully described by a track list and two span utilities:
<div class="grid grid-cols-[repeat(3,118px)] grid-rows-[repeat(3,118px)] gap-3">
<div class="row-span-2 …">channels</div>
<div class="col-span-2 …">revenue</div>Named areas earn their keep when the layout changes shape across breakpoints; for a fixed grid they add a naming layer with nothing behind it.
The nested stack needs one tile to break its own clipping. Every tile is overflow-hidden so glows stay inside — except the channels tile, which is overflow-visible and lifts to hover:z-20 so its fan can cross the cell boundary. Being able to see that exception on the element, rather than in a modifier class three rules away, is the practical gain.
The fan is the index pattern again. Each mini-card carries --i and moves by a multiple of it. Nothing about it is new — which is the point: after thirty demos the same three lines handle a fan, a stair, a tower and a pile.
Decorative data must say so. The sparkline is a row of divs with inline heights and the whole group is aria-hidden; the number beside it is the real content. A chart that means something needs a text equivalent, and one that does not should be silent.
Make it yours
- Change the bento shape by editing the track lists and the span utilities — no area names to keep in sync.
- Adjust the fan distance via the
-58pxmultiplier in the mini-card transform. - Feed the sparkline real values by editing the inline heights, or swap it for an SVG path.
- Recolour each tile's accent glow independently; the tile shell is shared.
- Add a tile and the grid absorbs it — the layout has no fixed child count.
Gotchas — read before shipping
- Set
pointer-events-noneon the decorative mini-cards, or they swallow the hover on the tile that is meant to be fanning them. - Only the fanning tile may be
overflow-visible; leaving them all unclipped lets every glow bleed across the grid. - Raise
z-indexon the fanning tile too —overflow-visiblealone still paints it under later siblings. - Test text contrast per tile. Dashboard tiles vary in luminance and one dim tile can quietly fail while the others pass.
- Mark decorative charts
aria-hidden. A bar row with no labels announced as a list of empty divs is worse than silence. - Bento layouts are strongly trend-coded; for an evergreen product a plainer grid ages better.
Browser support
| Chrome | Safari | Firefox | Edge |
|---|---|---|---|
| 111+ | 16.4+ | 128+ | 111+ |
CSS Grid with explicit track lists and spans is universal (Chrome 57+, Safari 10.1+, Firefox 52+). The floor is Tailwind v4's own generated sheet.