04 / 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 Stacked Cards Expand Accordion on Click
A pile resting on its bottom edges that opens by animating grid rows, no JS. The 0fr → 1fr reveal is grid-rows-[0fr] with peer-checked:grid-rows-[1fr] — a real utility pair, not an arbitrary property. The open card lifts clear via has-checked:, which is exactly the :has() rule the CSS version needs, only typed on the panel it applies to.
Untitled UI Pro: 5,000+ React + Tailwind CSS v4 components · from $349 one-time
Untitled UI React · Affiliate link
The code
<section class="stk-04 w-full min-h-svh flex items-center justify-center px-5 py-12 font-stk04-sans bg-stk04-bg" aria-label="Stacked expansion accordion">
<div class="w-[min(440px,100%)]">
<div class="relative -mt-3 first:mt-0 rounded-2xl border border-stk04-line bg-stk04-card shadow-[0_14px_30px_-22px_rgba(0,0,0,0.8)] transition-[margin,box-shadow,border-color] duration-350 has-checked:mt-3.5 first:has-checked:mt-0 has-checked:z-2 has-checked:border-stk04-accent has-checked:shadow-[0_22px_46px_-26px_var(--color-stk04-accent)] motion-reduce:transition-none">
<input class="peer sr-only" type="radio" name="stk-04" id="stk-04-1" checked>
<label class="flex items-center gap-3 px-5 py-4.5 cursor-pointer rounded-2xl text-[15px] font-bold text-stk04-dim peer-checked:text-white peer-focus-visible:outline-3 peer-focus-visible:outline-stk04-accent peer-focus-visible:-outline-offset-3" for="stk-04-1">
<span class="size-2 shrink-0 rounded-full bg-stk04-line transition-colors duration-300 peer-checked:bg-stk04-accent"></span>
Dimensions & weight
<span class="ml-auto font-stk04-mono text-2xl leading-none text-stk04-muted transition-[transform,color] duration-300 peer-checked:rotate-90 peer-checked:text-stk04-accent motion-reduce:transition-none" aria-hidden="true">›</span>
</label>
<div class="grid grid-rows-[0fr] transition-[grid-template-rows] duration-400 ease-stk04 peer-checked:grid-rows-[1fr] motion-reduce:transition-none">
<div class="overflow-hidden"><p class="px-5 pb-5 text-sm leading-relaxed text-stk04-muted">142 × 68 × 7.4 mm · 168 g. Aerospace-grade aluminium frame with a matte bead-blast finish.</p></div>
</div>
</div>
<div class="relative -mt-3 rounded-2xl border border-stk04-line bg-stk04-card shadow-[0_14px_30px_-22px_rgba(0,0,0,0.8)] transition-[margin,box-shadow,border-color] duration-350 has-checked:mt-3.5 has-checked:z-2 has-checked:border-stk04-accent has-checked:shadow-[0_22px_46px_-26px_var(--color-stk04-accent)] motion-reduce:transition-none">
<input class="peer sr-only" type="radio" name="stk-04" id="stk-04-2">
<label class="flex items-center gap-3 px-5 py-4.5 cursor-pointer rounded-2xl text-[15px] font-bold text-stk04-dim peer-checked:text-white peer-focus-visible:outline-3 peer-focus-visible:outline-stk04-accent peer-focus-visible:-outline-offset-3" for="stk-04-2">
<span class="size-2 shrink-0 rounded-full bg-stk04-line transition-colors duration-300 peer-checked:bg-stk04-accent"></span>
Display
<span class="ml-auto font-stk04-mono text-2xl leading-none text-stk04-muted transition-[transform,color] duration-300 peer-checked:rotate-90 peer-checked:text-stk04-accent motion-reduce:transition-none" aria-hidden="true">›</span>
</label>
<div class="grid grid-rows-[0fr] transition-[grid-template-rows] duration-400 ease-stk04 peer-checked:grid-rows-[1fr] motion-reduce:transition-none">
<div class="overflow-hidden"><p class="px-5 pb-5 text-sm leading-relaxed text-stk04-muted">6.3″ LTPO OLED, 1–120 Hz adaptive, 2600 nits peak, Dolby Vision, hardened glass.</p></div>
</div>
</div>
<div class="relative -mt-3 rounded-2xl border border-stk04-line bg-stk04-card shadow-[0_14px_30px_-22px_rgba(0,0,0,0.8)] transition-[margin,box-shadow,border-color] duration-350 has-checked:mt-3.5 has-checked:z-2 has-checked:border-stk04-accent has-checked:shadow-[0_22px_46px_-26px_var(--color-stk04-accent)] motion-reduce:transition-none">
<input class="peer sr-only" type="radio" name="stk-04" id="stk-04-3">
<label class="flex items-center gap-3 px-5 py-4.5 cursor-pointer rounded-2xl text-[15px] font-bold text-stk04-dim peer-checked:text-white peer-focus-visible:outline-3 peer-focus-visible:outline-stk04-accent peer-focus-visible:-outline-offset-3" for="stk-04-3">
<span class="size-2 shrink-0 rounded-full bg-stk04-line transition-colors duration-300 peer-checked:bg-stk04-accent"></span>
Battery & charging
<span class="ml-auto font-stk04-mono text-2xl leading-none text-stk04-muted transition-[transform,color] duration-300 peer-checked:rotate-90 peer-checked:text-stk04-accent motion-reduce:transition-none" aria-hidden="true">›</span>
</label>
<div class="grid grid-rows-[0fr] transition-[grid-template-rows] duration-400 ease-stk04 peer-checked:grid-rows-[1fr] motion-reduce:transition-none">
<div class="overflow-hidden"><p class="px-5 pb-5 text-sm leading-relaxed text-stk04-muted">4,800 mAh · 80 W wired to 50% in 12 min · 30 W wireless · USB-C 3.2.</p></div>
</div>
</div>
<div class="relative -mt-3 rounded-2xl border border-stk04-line bg-stk04-card shadow-[0_14px_30px_-22px_rgba(0,0,0,0.8)] transition-[margin,box-shadow,border-color] duration-350 has-checked:mt-3.5 has-checked:z-2 has-checked:border-stk04-accent has-checked:shadow-[0_22px_46px_-26px_var(--color-stk04-accent)] motion-reduce:transition-none">
<input class="peer sr-only" type="radio" name="stk-04" id="stk-04-4">
<label class="flex items-center gap-3 px-5 py-4.5 cursor-pointer rounded-2xl text-[15px] font-bold text-stk04-dim peer-checked:text-white peer-focus-visible:outline-3 peer-focus-visible:outline-stk04-accent peer-focus-visible:-outline-offset-3" for="stk-04-4">
<span class="size-2 shrink-0 rounded-full bg-stk04-line transition-colors duration-300 peer-checked:bg-stk04-accent"></span>
In the box
<span class="ml-auto font-stk04-mono text-2xl leading-none text-stk04-muted transition-[transform,color] duration-300 peer-checked:rotate-90 peer-checked:text-stk04-accent motion-reduce:transition-none" aria-hidden="true">›</span>
</label>
<div class="grid grid-rows-[0fr] transition-[grid-template-rows] duration-400 ease-stk04 peer-checked:grid-rows-[1fr] motion-reduce:transition-none">
<div class="overflow-hidden"><p class="px-5 pb-5 text-sm leading-relaxed text-stk04-muted">Device, braided USB-C cable, SIM tool, quick-start guide. Adapter sold separately.</p></div>
</div>
</div>
</div>
</section><section class="stk-04 w-full min-h-svh flex items-center justify-center px-5 py-12 font-stk04-sans bg-stk04-bg" aria-label="Stacked expansion accordion">
<div class="w-[min(440px,100%)]">
<div class="relative -mt-3 first:mt-0 rounded-2xl border border-stk04-line bg-stk04-card shadow-[0_14px_30px_-22px_rgba(0,0,0,0.8)] transition-[margin,box-shadow,border-color] duration-350 has-checked:mt-3.5 first:has-checked:mt-0 has-checked:z-2 has-checked:border-stk04-accent has-checked:shadow-[0_22px_46px_-26px_var(--color-stk04-accent)] motion-reduce:transition-none">
<input class="peer sr-only" type="radio" name="stk-04" id="stk-04-1" checked>
<label class="flex items-center gap-3 px-5 py-4.5 cursor-pointer rounded-2xl text-[15px] font-bold text-stk04-dim peer-checked:text-white peer-focus-visible:outline-3 peer-focus-visible:outline-stk04-accent peer-focus-visible:-outline-offset-3" for="stk-04-1">
<span class="size-2 shrink-0 rounded-full bg-stk04-line transition-colors duration-300 peer-checked:bg-stk04-accent"></span>
Dimensions & weight
<span class="ml-auto font-stk04-mono text-2xl leading-none text-stk04-muted transition-[transform,color] duration-300 peer-checked:rotate-90 peer-checked:text-stk04-accent motion-reduce:transition-none" aria-hidden="true">›</span>
</label>
<div class="grid grid-rows-[0fr] transition-[grid-template-rows] duration-400 ease-stk04 peer-checked:grid-rows-[1fr] motion-reduce:transition-none">
<div class="overflow-hidden"><p class="px-5 pb-5 text-sm leading-relaxed text-stk04-muted">142 × 68 × 7.4 mm · 168 g. Aerospace-grade aluminium frame with a matte bead-blast finish.</p></div>
</div>
</div>
<div class="relative -mt-3 rounded-2xl border border-stk04-line bg-stk04-card shadow-[0_14px_30px_-22px_rgba(0,0,0,0.8)] transition-[margin,box-shadow,border-color] duration-350 has-checked:mt-3.5 has-checked:z-2 has-checked:border-stk04-accent has-checked:shadow-[0_22px_46px_-26px_var(--color-stk04-accent)] motion-reduce:transition-none">
<input class="peer sr-only" type="radio" name="stk-04" id="stk-04-2">
<label class="flex items-center gap-3 px-5 py-4.5 cursor-pointer rounded-2xl text-[15px] font-bold text-stk04-dim peer-checked:text-white peer-focus-visible:outline-3 peer-focus-visible:outline-stk04-accent peer-focus-visible:-outline-offset-3" for="stk-04-2">
<span class="size-2 shrink-0 rounded-full bg-stk04-line transition-colors duration-300 peer-checked:bg-stk04-accent"></span>
Display
<span class="ml-auto font-stk04-mono text-2xl leading-none text-stk04-muted transition-[transform,color] duration-300 peer-checked:rotate-90 peer-checked:text-stk04-accent motion-reduce:transition-none" aria-hidden="true">›</span>
</label>
<div class="grid grid-rows-[0fr] transition-[grid-template-rows] duration-400 ease-stk04 peer-checked:grid-rows-[1fr] motion-reduce:transition-none">
<div class="overflow-hidden"><p class="px-5 pb-5 text-sm leading-relaxed text-stk04-muted">6.3″ LTPO OLED, 1–120 Hz adaptive, 2600 nits peak, Dolby Vision, hardened glass.</p></div>
</div>
</div>
<div class="relative -mt-3 rounded-2xl border border-stk04-line bg-stk04-card shadow-[0_14px_30px_-22px_rgba(0,0,0,0.8)] transition-[margin,box-shadow,border-color] duration-350 has-checked:mt-3.5 has-checked:z-2 has-checked:border-stk04-accent has-checked:shadow-[0_22px_46px_-26px_var(--color-stk04-accent)] motion-reduce:transition-none">
<input class="peer sr-only" type="radio" name="stk-04" id="stk-04-3">
<label class="flex items-center gap-3 px-5 py-4.5 cursor-pointer rounded-2xl text-[15px] font-bold text-stk04-dim peer-checked:text-white peer-focus-visible:outline-3 peer-focus-visible:outline-stk04-accent peer-focus-visible:-outline-offset-3" for="stk-04-3">
<span class="size-2 shrink-0 rounded-full bg-stk04-line transition-colors duration-300 peer-checked:bg-stk04-accent"></span>
Battery & charging
<span class="ml-auto font-stk04-mono text-2xl leading-none text-stk04-muted transition-[transform,color] duration-300 peer-checked:rotate-90 peer-checked:text-stk04-accent motion-reduce:transition-none" aria-hidden="true">›</span>
</label>
<div class="grid grid-rows-[0fr] transition-[grid-template-rows] duration-400 ease-stk04 peer-checked:grid-rows-[1fr] motion-reduce:transition-none">
<div class="overflow-hidden"><p class="px-5 pb-5 text-sm leading-relaxed text-stk04-muted">4,800 mAh · 80 W wired to 50% in 12 min · 30 W wireless · USB-C 3.2.</p></div>
</div>
</div>
<div class="relative -mt-3 rounded-2xl border border-stk04-line bg-stk04-card shadow-[0_14px_30px_-22px_rgba(0,0,0,0.8)] transition-[margin,box-shadow,border-color] duration-350 has-checked:mt-3.5 has-checked:z-2 has-checked:border-stk04-accent has-checked:shadow-[0_22px_46px_-26px_var(--color-stk04-accent)] motion-reduce:transition-none">
<input class="peer sr-only" type="radio" name="stk-04" id="stk-04-4">
<label class="flex items-center gap-3 px-5 py-4.5 cursor-pointer rounded-2xl text-[15px] font-bold text-stk04-dim peer-checked:text-white peer-focus-visible:outline-3 peer-focus-visible:outline-stk04-accent peer-focus-visible:-outline-offset-3" for="stk-04-4">
<span class="size-2 shrink-0 rounded-full bg-stk04-line transition-colors duration-300 peer-checked:bg-stk04-accent"></span>
In the box
<span class="ml-auto font-stk04-mono text-2xl leading-none text-stk04-muted transition-[transform,color] duration-300 peer-checked:rotate-90 peer-checked:text-stk04-accent motion-reduce:transition-none" aria-hidden="true">›</span>
</label>
<div class="grid grid-rows-[0fr] transition-[grid-template-rows] duration-400 ease-stk04 peer-checked:grid-rows-[1fr] motion-reduce:transition-none">
<div class="overflow-hidden"><p class="px-5 pb-5 text-sm leading-relaxed text-stk04-muted">Device, braided USB-C cable, SIM tool, quick-start guide. Adapter sold separately.</p></div>
</div>
</div>
</div>
</section>@import "tailwindcss";
@theme {
--color-stk04-bg: #111319;
--color-stk04-bg: oklch(0.20 0.012 265);
--color-stk04-card: #1c1f28;
--color-stk04-card: oklch(0.25 0.014 265);
--color-stk04-line: #2a2e3a;
--color-stk04-line: oklch(0.32 0.016 265);
--color-stk04-dim: #eef0f6;
--color-stk04-dim: oklch(0.94 0.006 265);
--color-stk04-muted: #aab0c0;
--color-stk04-muted: oklch(0.75 0.018 265);
--color-stk04-accent: #6a7bf0;
--color-stk04-accent: oklch(0.60 0.16 265);
--font-stk04-sans: 'Archivo', system-ui, sans-serif;
--font-stk04-mono: 'JetBrains Mono', ui-monospace, monospace;
--ease-stk04: cubic-bezier(0.3, 0.9, 0.3, 1);
}@import "tailwindcss";
@theme {
--color-stk04-bg: #111319;
--color-stk04-bg: oklch(0.20 0.012 265);
--color-stk04-card: #1c1f28;
--color-stk04-card: oklch(0.25 0.014 265);
--color-stk04-line: #2a2e3a;
--color-stk04-line: oklch(0.32 0.016 265);
--color-stk04-dim: #eef0f6;
--color-stk04-dim: oklch(0.94 0.006 265);
--color-stk04-muted: #aab0c0;
--color-stk04-muted: oklch(0.75 0.018 265);
--color-stk04-accent: #6a7bf0;
--color-stk04-accent: oklch(0.60 0.16 265);
--font-stk04-sans: 'Archivo', system-ui, sans-serif;
--font-stk04-mono: 'JetBrains Mono', ui-monospace, monospace;
--ease-stk04: 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-stk04-bg: #111319;
--color-stk04-bg: oklch(0.20 0.012 265);
--color-stk04-card: #1c1f28;
--color-stk04-card: oklch(0.25 0.014 265);
--color-stk04-line: #2a2e3a;
--color-stk04-line: oklch(0.32 0.016 265);
--color-stk04-dim: #eef0f6;
--color-stk04-dim: oklch(0.94 0.006 265);
--color-stk04-muted: #aab0c0;
--color-stk04-muted: oklch(0.75 0.018 265);
--color-stk04-accent: #6a7bf0;
--color-stk04-accent: oklch(0.60 0.16 265);
--font-stk04-sans: 'Archivo', system-ui, sans-serif;
--font-stk04-mono: 'JetBrains Mono', ui-monospace, monospace;
--ease-stk04: cubic-bezier(0.3, 0.9, 0.3, 1);
}@import "tailwindcss";
@theme {
--color-stk04-bg: #111319;
--color-stk04-bg: oklch(0.20 0.012 265);
--color-stk04-card: #1c1f28;
--color-stk04-card: oklch(0.25 0.014 265);
--color-stk04-line: #2a2e3a;
--color-stk04-line: oklch(0.32 0.016 265);
--color-stk04-dim: #eef0f6;
--color-stk04-dim: oklch(0.94 0.006 265);
--color-stk04-muted: #aab0c0;
--color-stk04-muted: oklch(0.75 0.018 265);
--color-stk04-accent: #6a7bf0;
--color-stk04-accent: oklch(0.60 0.16 265);
--font-stk04-sans: 'Archivo', system-ui, sans-serif;
--font-stk04-mono: 'JetBrains Mono', ui-monospace, monospace;
--ease-stk04: 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=Archivo:wdth,wght@75..125,400;75..125,600;75..125,700&family=JetBrains+Mono:wght@400;500&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=Archivo:wdth,wght@75..125,400;75..125,600;75..125,700&family=JetBrains+Mono:wght@400;500&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. - This is the first demo in the collection whose stylesheet is nothing but
@theme— every selector in the CSS version, including the:has()lift, has an exact variant. grid-template-rows:0fr→1fris a first-class utility pair here (grid-rows-[0fr]/peer-checked:grid-rows-[1fr]), not an arbitrary property.- The four
.radio:checked ~ …rules becomepeer-checked:on the header, the dot and the chevron; the panel's:has()rule becomeshas-checked:on the panel. peer-*versushas-*is the whole port: the radio is a sibling of the header but a descendant of the panel, so each needs a different family of variant.- The scoped sr-only clip becomes Tailwind's own
sr-only, which keeps the radios focusable so native arrow-key navigation survives. - The border-plus-shadow active state reads
--color-stk04-accentin both places, including inside an arbitrary shadow —shadow-[0_22px_46px_-26px_var(--color-stk04-accent)].
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 Stacked Cards Expand Accordion on Click
Source: https://codefronts.com/components/tailwind-stacked-cards/tailwind-stacked-cards-expand-accordion-on-click/
A pile resting on its bottom edges that opens by animating grid rows, no JS. The 0fr → 1fr reveal is grid-rows-[0fr] with peer-checked:grid-rows-[1fr] — a real utility pair, not an arbitrary property. The open card lifts clear via has-checked:, which is exactly the :has() rule the CSS version needs, only typed on the panel it applies to.
## HTML
```html
<section class="stk-04 w-full min-h-svh flex items-center justify-center px-5 py-12 font-stk04-sans bg-stk04-bg" aria-label="Stacked expansion accordion">
<div class="w-[min(440px,100%)]">
<div class="relative -mt-3 first:mt-0 rounded-2xl border border-stk04-line bg-stk04-card shadow-[0_14px_30px_-22px_rgba(0,0,0,0.8)] transition-[margin,box-shadow,border-color] duration-350 has-checked:mt-3.5 first:has-checked:mt-0 has-checked:z-2 has-checked:border-stk04-accent has-checked:shadow-[0_22px_46px_-26px_var(--color-stk04-accent)] motion-reduce:transition-none">
<input class="peer sr-only" type="radio" name="stk-04" id="stk-04-1" checked>
<label class="flex items-center gap-3 px-5 py-4.5 cursor-pointer rounded-2xl text-[15px] font-bold text-stk04-dim peer-checked:text-white peer-focus-visible:outline-3 peer-focus-visible:outline-stk04-accent peer-focus-visible:-outline-offset-3" for="stk-04-1">
<span class="size-2 shrink-0 rounded-full bg-stk04-line transition-colors duration-300 peer-checked:bg-stk04-accent"></span>
Dimensions & weight
<span class="ml-auto font-stk04-mono text-2xl leading-none text-stk04-muted transition-[transform,color] duration-300 peer-checked:rotate-90 peer-checked:text-stk04-accent motion-reduce:transition-none" aria-hidden="true">›</span>
</label>
<div class="grid grid-rows-[0fr] transition-[grid-template-rows] duration-400 ease-stk04 peer-checked:grid-rows-[1fr] motion-reduce:transition-none">
<div class="overflow-hidden"><p class="px-5 pb-5 text-sm leading-relaxed text-stk04-muted">142 × 68 × 7.4 mm · 168 g. Aerospace-grade aluminium frame with a matte bead-blast finish.</p></div>
</div>
</div>
<div class="relative -mt-3 rounded-2xl border border-stk04-line bg-stk04-card shadow-[0_14px_30px_-22px_rgba(0,0,0,0.8)] transition-[margin,box-shadow,border-color] duration-350 has-checked:mt-3.5 has-checked:z-2 has-checked:border-stk04-accent has-checked:shadow-[0_22px_46px_-26px_var(--color-stk04-accent)] motion-reduce:transition-none">
<input class="peer sr-only" type="radio" name="stk-04" id="stk-04-2">
<label class="flex items-center gap-3 px-5 py-4.5 cursor-pointer rounded-2xl text-[15px] font-bold text-stk04-dim peer-checked:text-white peer-focus-visible:outline-3 peer-focus-visible:outline-stk04-accent peer-focus-visible:-outline-offset-3" for="stk-04-2">
<span class="size-2 shrink-0 rounded-full bg-stk04-line transition-colors duration-300 peer-checked:bg-stk04-accent"></span>
Display
<span class="ml-auto font-stk04-mono text-2xl leading-none text-stk04-muted transition-[transform,color] duration-300 peer-checked:rotate-90 peer-checked:text-stk04-accent motion-reduce:transition-none" aria-hidden="true">›</span>
</label>
<div class="grid grid-rows-[0fr] transition-[grid-template-rows] duration-400 ease-stk04 peer-checked:grid-rows-[1fr] motion-reduce:transition-none">
<div class="overflow-hidden"><p class="px-5 pb-5 text-sm leading-relaxed text-stk04-muted">6.3″ LTPO OLED, 1–120 Hz adaptive, 2600 nits peak, Dolby Vision, hardened glass.</p></div>
</div>
</div>
<div class="relative -mt-3 rounded-2xl border border-stk04-line bg-stk04-card shadow-[0_14px_30px_-22px_rgba(0,0,0,0.8)] transition-[margin,box-shadow,border-color] duration-350 has-checked:mt-3.5 has-checked:z-2 has-checked:border-stk04-accent has-checked:shadow-[0_22px_46px_-26px_var(--color-stk04-accent)] motion-reduce:transition-none">
<input class="peer sr-only" type="radio" name="stk-04" id="stk-04-3">
<label class="flex items-center gap-3 px-5 py-4.5 cursor-pointer rounded-2xl text-[15px] font-bold text-stk04-dim peer-checked:text-white peer-focus-visible:outline-3 peer-focus-visible:outline-stk04-accent peer-focus-visible:-outline-offset-3" for="stk-04-3">
<span class="size-2 shrink-0 rounded-full bg-stk04-line transition-colors duration-300 peer-checked:bg-stk04-accent"></span>
Battery & charging
<span class="ml-auto font-stk04-mono text-2xl leading-none text-stk04-muted transition-[transform,color] duration-300 peer-checked:rotate-90 peer-checked:text-stk04-accent motion-reduce:transition-none" aria-hidden="true">›</span>
</label>
<div class="grid grid-rows-[0fr] transition-[grid-template-rows] duration-400 ease-stk04 peer-checked:grid-rows-[1fr] motion-reduce:transition-none">
<div class="overflow-hidden"><p class="px-5 pb-5 text-sm leading-relaxed text-stk04-muted">4,800 mAh · 80 W wired to 50% in 12 min · 30 W wireless · USB-C 3.2.</p></div>
</div>
</div>
<div class="relative -mt-3 rounded-2xl border border-stk04-line bg-stk04-card shadow-[0_14px_30px_-22px_rgba(0,0,0,0.8)] transition-[margin,box-shadow,border-color] duration-350 has-checked:mt-3.5 has-checked:z-2 has-checked:border-stk04-accent has-checked:shadow-[0_22px_46px_-26px_var(--color-stk04-accent)] motion-reduce:transition-none">
<input class="peer sr-only" type="radio" name="stk-04" id="stk-04-4">
<label class="flex items-center gap-3 px-5 py-4.5 cursor-pointer rounded-2xl text-[15px] font-bold text-stk04-dim peer-checked:text-white peer-focus-visible:outline-3 peer-focus-visible:outline-stk04-accent peer-focus-visible:-outline-offset-3" for="stk-04-4">
<span class="size-2 shrink-0 rounded-full bg-stk04-line transition-colors duration-300 peer-checked:bg-stk04-accent"></span>
In the box
<span class="ml-auto font-stk04-mono text-2xl leading-none text-stk04-muted transition-[transform,color] duration-300 peer-checked:rotate-90 peer-checked:text-stk04-accent motion-reduce:transition-none" aria-hidden="true">›</span>
</label>
<div class="grid grid-rows-[0fr] transition-[grid-template-rows] duration-400 ease-stk04 peer-checked:grid-rows-[1fr] motion-reduce:transition-none">
<div class="overflow-hidden"><p class="px-5 pb-5 text-sm leading-relaxed text-stk04-muted">Device, braided USB-C cable, SIM tool, quick-start guide. Adapter sold separately.</p></div>
</div>
</div>
</div>
</section>
```
## CSS
```css
@import "tailwindcss";
@theme {
--color-stk04-bg: #111319;
--color-stk04-bg: oklch(0.20 0.012 265);
--color-stk04-card: #1c1f28;
--color-stk04-card: oklch(0.25 0.014 265);
--color-stk04-line: #2a2e3a;
--color-stk04-line: oklch(0.32 0.016 265);
--color-stk04-dim: #eef0f6;
--color-stk04-dim: oklch(0.94 0.006 265);
--color-stk04-muted: #aab0c0;
--color-stk04-muted: oklch(0.75 0.018 265);
--color-stk04-accent: #6a7bf0;
--color-stk04-accent: oklch(0.60 0.16 265);
--font-stk04-sans: 'Archivo', system-ui, sans-serif;
--font-stk04-mono: 'JetBrains Mono', ui-monospace, monospace;
--ease-stk04: 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 Stacked Cards Expand Accordion on Click
Source: https://codefronts.com/components/tailwind-stacked-cards/tailwind-stacked-cards-expand-accordion-on-click/
A pile resting on its bottom edges that opens by animating grid rows, no JS. The 0fr → 1fr reveal is grid-rows-[0fr] with peer-checked:grid-rows-[1fr] — a real utility pair, not an arbitrary property. The open card lifts clear via has-checked:, which is exactly the :has() rule the CSS version needs, only typed on the panel it applies to.
## HTML
```html
<section class="stk-04 w-full min-h-svh flex items-center justify-center px-5 py-12 font-stk04-sans bg-stk04-bg" aria-label="Stacked expansion accordion">
<div class="w-[min(440px,100%)]">
<div class="relative -mt-3 first:mt-0 rounded-2xl border border-stk04-line bg-stk04-card shadow-[0_14px_30px_-22px_rgba(0,0,0,0.8)] transition-[margin,box-shadow,border-color] duration-350 has-checked:mt-3.5 first:has-checked:mt-0 has-checked:z-2 has-checked:border-stk04-accent has-checked:shadow-[0_22px_46px_-26px_var(--color-stk04-accent)] motion-reduce:transition-none">
<input class="peer sr-only" type="radio" name="stk-04" id="stk-04-1" checked>
<label class="flex items-center gap-3 px-5 py-4.5 cursor-pointer rounded-2xl text-[15px] font-bold text-stk04-dim peer-checked:text-white peer-focus-visible:outline-3 peer-focus-visible:outline-stk04-accent peer-focus-visible:-outline-offset-3" for="stk-04-1">
<span class="size-2 shrink-0 rounded-full bg-stk04-line transition-colors duration-300 peer-checked:bg-stk04-accent"></span>
Dimensions & weight
<span class="ml-auto font-stk04-mono text-2xl leading-none text-stk04-muted transition-[transform,color] duration-300 peer-checked:rotate-90 peer-checked:text-stk04-accent motion-reduce:transition-none" aria-hidden="true">›</span>
</label>
<div class="grid grid-rows-[0fr] transition-[grid-template-rows] duration-400 ease-stk04 peer-checked:grid-rows-[1fr] motion-reduce:transition-none">
<div class="overflow-hidden"><p class="px-5 pb-5 text-sm leading-relaxed text-stk04-muted">142 × 68 × 7.4 mm · 168 g. Aerospace-grade aluminium frame with a matte bead-blast finish.</p></div>
</div>
</div>
<div class="relative -mt-3 rounded-2xl border border-stk04-line bg-stk04-card shadow-[0_14px_30px_-22px_rgba(0,0,0,0.8)] transition-[margin,box-shadow,border-color] duration-350 has-checked:mt-3.5 has-checked:z-2 has-checked:border-stk04-accent has-checked:shadow-[0_22px_46px_-26px_var(--color-stk04-accent)] motion-reduce:transition-none">
<input class="peer sr-only" type="radio" name="stk-04" id="stk-04-2">
<label class="flex items-center gap-3 px-5 py-4.5 cursor-pointer rounded-2xl text-[15px] font-bold text-stk04-dim peer-checked:text-white peer-focus-visible:outline-3 peer-focus-visible:outline-stk04-accent peer-focus-visible:-outline-offset-3" for="stk-04-2">
<span class="size-2 shrink-0 rounded-full bg-stk04-line transition-colors duration-300 peer-checked:bg-stk04-accent"></span>
Display
<span class="ml-auto font-stk04-mono text-2xl leading-none text-stk04-muted transition-[transform,color] duration-300 peer-checked:rotate-90 peer-checked:text-stk04-accent motion-reduce:transition-none" aria-hidden="true">›</span>
</label>
<div class="grid grid-rows-[0fr] transition-[grid-template-rows] duration-400 ease-stk04 peer-checked:grid-rows-[1fr] motion-reduce:transition-none">
<div class="overflow-hidden"><p class="px-5 pb-5 text-sm leading-relaxed text-stk04-muted">6.3″ LTPO OLED, 1–120 Hz adaptive, 2600 nits peak, Dolby Vision, hardened glass.</p></div>
</div>
</div>
<div class="relative -mt-3 rounded-2xl border border-stk04-line bg-stk04-card shadow-[0_14px_30px_-22px_rgba(0,0,0,0.8)] transition-[margin,box-shadow,border-color] duration-350 has-checked:mt-3.5 has-checked:z-2 has-checked:border-stk04-accent has-checked:shadow-[0_22px_46px_-26px_var(--color-stk04-accent)] motion-reduce:transition-none">
<input class="peer sr-only" type="radio" name="stk-04" id="stk-04-3">
<label class="flex items-center gap-3 px-5 py-4.5 cursor-pointer rounded-2xl text-[15px] font-bold text-stk04-dim peer-checked:text-white peer-focus-visible:outline-3 peer-focus-visible:outline-stk04-accent peer-focus-visible:-outline-offset-3" for="stk-04-3">
<span class="size-2 shrink-0 rounded-full bg-stk04-line transition-colors duration-300 peer-checked:bg-stk04-accent"></span>
Battery & charging
<span class="ml-auto font-stk04-mono text-2xl leading-none text-stk04-muted transition-[transform,color] duration-300 peer-checked:rotate-90 peer-checked:text-stk04-accent motion-reduce:transition-none" aria-hidden="true">›</span>
</label>
<div class="grid grid-rows-[0fr] transition-[grid-template-rows] duration-400 ease-stk04 peer-checked:grid-rows-[1fr] motion-reduce:transition-none">
<div class="overflow-hidden"><p class="px-5 pb-5 text-sm leading-relaxed text-stk04-muted">4,800 mAh · 80 W wired to 50% in 12 min · 30 W wireless · USB-C 3.2.</p></div>
</div>
</div>
<div class="relative -mt-3 rounded-2xl border border-stk04-line bg-stk04-card shadow-[0_14px_30px_-22px_rgba(0,0,0,0.8)] transition-[margin,box-shadow,border-color] duration-350 has-checked:mt-3.5 has-checked:z-2 has-checked:border-stk04-accent has-checked:shadow-[0_22px_46px_-26px_var(--color-stk04-accent)] motion-reduce:transition-none">
<input class="peer sr-only" type="radio" name="stk-04" id="stk-04-4">
<label class="flex items-center gap-3 px-5 py-4.5 cursor-pointer rounded-2xl text-[15px] font-bold text-stk04-dim peer-checked:text-white peer-focus-visible:outline-3 peer-focus-visible:outline-stk04-accent peer-focus-visible:-outline-offset-3" for="stk-04-4">
<span class="size-2 shrink-0 rounded-full bg-stk04-line transition-colors duration-300 peer-checked:bg-stk04-accent"></span>
In the box
<span class="ml-auto font-stk04-mono text-2xl leading-none text-stk04-muted transition-[transform,color] duration-300 peer-checked:rotate-90 peer-checked:text-stk04-accent motion-reduce:transition-none" aria-hidden="true">›</span>
</label>
<div class="grid grid-rows-[0fr] transition-[grid-template-rows] duration-400 ease-stk04 peer-checked:grid-rows-[1fr] motion-reduce:transition-none">
<div class="overflow-hidden"><p class="px-5 pb-5 text-sm leading-relaxed text-stk04-muted">Device, braided USB-C cable, SIM tool, quick-start guide. Adapter sold separately.</p></div>
</div>
</div>
</div>
</section>
```
## CSS
```css
@import "tailwindcss";
@theme {
--color-stk04-bg: #111319;
--color-stk04-bg: oklch(0.20 0.012 265);
--color-stk04-card: #1c1f28;
--color-stk04-card: oklch(0.25 0.014 265);
--color-stk04-line: #2a2e3a;
--color-stk04-line: oklch(0.32 0.016 265);
--color-stk04-dim: #eef0f6;
--color-stk04-dim: oklch(0.94 0.006 265);
--color-stk04-muted: #aab0c0;
--color-stk04-muted: oklch(0.75 0.018 265);
--color-stk04-accent: #6a7bf0;
--color-stk04-accent: oklch(0.60 0.16 265);
--font-stk04-sans: 'Archivo', system-ui, sans-serif;
--font-stk04-mono: 'JetBrains Mono', ui-monospace, monospace;
--ease-stk04: cubic-bezier(0.3, 0.9, 0.3, 1);
}
```How this works
The reveal is a grid utility, not a hack. height:auto cannot be transitioned, so the body sits in a one-row grid that animates between fractions. Tailwind has a real scale for this:
<div class="grid grid-rows-[0fr] peer-checked:grid-rows-[1fr]
transition-[grid-template-rows] duration-400">
<div class="overflow-hidden">…</div>
</div>The inner overflow-hidden is mandatory. Without it the collapsed row is zero-height but its content still paints straight through.
peer and has-checked divide the work. The radio is a previous sibling of the header, so peer-checked: reaches the header, the status dot and the chevron. It is a descendant of the panel, so the panel cannot use peer-* and uses has-checked:mt-3.5 has-checked:border-stk04-accent instead. Getting this pairing wrong is the most common port bug in radio-driven CSS: peer-* only ever looks at earlier siblings.
Hidden but focusable. The radios use sr-only, which clips them without removing them from the tab order, so native arrow-key navigation between radios still works. The visible ring is drawn on the label with peer-focus-visible:outline-3, since the element that actually receives focus has no box to paint.
The stack look is document flow, not absolute positioning. Panels overlap with -mt-3 and simply reflow when one grows — which is why this stack, unlike the absolute ones in this collection, needs no fixed height and never clips at small widths.
Make it yours
- Allow several panels open at once by switching the inputs to
type="checkbox"and dropping the sharedname; every variant in the markup keeps working. - Change the resting overlap with the
-mt-3value, and the open card's clearance withhas-checked:mt-3.5. - Retime the reveal via
duration-400on the grid wrapper. - Recolour the active state through
--color-stk04-accentin@theme— the border, the dot and the chevron all read it. - Start collapsed by removing
checkedfrom the first input; the layout does not depend on one being open.
Gotchas — read before shipping
- Animate
grid-template-rows, neverheight:auto. And name it:transition-alldoes not cover it reliably across engines. overflow-hiddenon the inner wrapper is required — the grid row collapses but the content does not clip itself.peer-*only matches previous siblings. The panel itself needshas-checked:; reaching forpeer-checked:there silently does nothing.- Use
sr-only, nothiddenoropacity-0 pointer-events-none, on the radios.display:noneremoves them from the tab order and kills arrow-key navigation. first:mt-0has to be repeated on thehas-checked:path (first:has-checked:mt-0), or the first panel jumps when it opens.- Give the label a real hit target.
py-4.5keeps every header above the 44px minimum on touch.
Browser support
| Chrome | Safari | Firefox | Edge |
|---|---|---|---|
| 111+ | 16.4+ | 128+ | 111+ |
Animating grid-template-rows to 0fr/1fr needs Chrome 107+, Safari 16+, Firefox 66+; older engines snap open without the tween. has-checked: compiles to :has(), which is inside Tailwind v4's own floor.