28 / 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 Hover Reveal Spread
The editorial portfolio deck: four gradient cards in a slight pile that fan into an arc when you approach, then lift individually. The port's real subject is the flicker guard — the fan must apply on stage hover except when a card itself is hovered, and no built-in variant expresses that, so it becomes one honest arbitrary variant. All four cards then share a single identical class list, with their destinations as custom properties.
Untitled UI Pro: 5,000+ React + Tailwind CSS v4 components · from $349 one-time
Untitled UI React · Affiliate link
The code
<section class="stk-28 relative grid w-full min-h-svh place-items-center overflow-hidden bg-stk28-void font-stk28-sans">
<div class="pointer-events-none absolute -inset-[30%] blur-[20px] animate-stk28-drift motion-reduce:animate-none [background-image:radial-gradient(40%_40%_at_20%_30%,var(--color-stk28-aur1),transparent_70%),radial-gradient(45%_45%_at_80%_25%,var(--color-stk28-aur2),transparent_70%),radial-gradient(50%_50%_at_50%_85%,var(--color-stk28-aur3),transparent_70%)]" aria-hidden="true"></div>
<div class="pointer-events-none absolute inset-0 mix-blend-overlay [background-image:repeating-linear-gradient(0deg,rgba(255,255,255,0.022)_0_1px,transparent_1px_3px)]" aria-hidden="true"></div>
<div class="stk-28__stage relative z-1 h-100 w-75 [perspective:1300px]" aria-label="Selected works, four case studies">
<div class="stk-28__card group absolute inset-0 cursor-pointer overflow-hidden rounded-3xl [transform-style:preserve-3d] [z-index:calc(4-var(--i))] brightness-[calc(1-var(--i)*0.13)] shadow-[0_30px_60px_rgba(0,0,0,0.55),inset_0_0_0_1px_rgba(255,255,255,0.08)] [transform:translateZ(calc(var(--i)*-40px))_translateY(calc(var(--i)*10px))_rotate(var(--r0))] transition-[transform,box-shadow,filter] duration-800 ease-stk28 [.stk-28__stage:hover:not(:has(.stk-28__card:hover))_&]:brightness-100 [.stk-28__stage:hover:not(:has(.stk-28__card:hover))_&]:[transform:translate(var(--fx),var(--fy))_translateZ(var(--fz))_rotate(var(--fr))] hover:z-9 hover:brightness-105 hover:shadow-[0_50px_90px_rgba(0,0,0,0.65),inset_0_0_0_1px_rgba(255,255,255,0.18),0_0_40px_rgba(255,255,255,0.12)] hover:[transform:translate(var(--fx),calc(var(--fy)-26px))_translateZ(90px)_rotate(var(--fr))_scale(1.06)] motion-reduce:transition-none" style="--i:0;--r0:-3deg;--fx:-165px;--fy:-30px;--fr:-12deg;--fz:40px">
<div class="absolute inset-0 backdrop-blur-[8px]" aria-hidden="true">
<div class="absolute inset-0 opacity-90 bg-[size:200%_200%] animate-stk28-flow motion-reduce:animate-none bg-linear-[135deg,var(--color-stk28-a1),var(--color-stk28-a2)_40%,var(--color-stk28-a3)]"></div>
<div class="absolute inset-0 opacity-0 mix-blend-soft-light transition-opacity duration-500 group-hover:opacity-100 [background-image:radial-gradient(280px_circle_at_50%_40%,rgba(255,255,255,0.4),transparent_60%)]"></div>
<div class="absolute -top-[60%] -left-[30%] h-[220%] w-[60%] [background-image:linear-gradient(90deg,transparent,rgba(255,255,255,0.5),transparent)] [transform:rotate(18deg)_translateX(-220px)] transition-transform duration-900 ease-stk28 group-hover:[transform:rotate(18deg)_translateX(560px)] motion-reduce:transition-none"></div>
</div>
<div class="absolute inset-0 flex flex-col justify-between p-7 [transform:translateZ(50px)]">
<div class="flex items-center gap-2.5">
<span class="rounded-full border border-white/25 bg-white/16 px-3 py-1.5 text-[0.62rem] font-medium tracking-[0.22em] uppercase text-white backdrop-blur-[6px]">Index</span>
<span class="ml-auto font-stk28-serif text-[1.3rem] italic text-white/75">01</span>
</div>
<div class="flex flex-col gap-4">
<p class="m-0 font-stk28-serif text-5xl leading-[0.92] tracking-[-0.01em] text-white [text-shadow:0_2px_20px_rgba(0,0,0,0.3)]">Selected<br><em class="italic">works</em></p>
<p class="m-0 max-w-[24ch] text-[0.8rem] leading-relaxed text-white/80">Hover to open the deck. Move across to explore each case.</p>
<span class="inline-flex translate-y-2 items-center gap-1.5 text-[0.78rem] text-white opacity-0 transition-[opacity,transform] duration-500 delay-100 group-hover:translate-y-0 group-hover:opacity-100 motion-reduce:transition-none">Begin <span class="grid size-5.5 place-items-center rounded-full bg-white/20 text-[0.8rem]" aria-hidden="true">→</span></span>
</div>
</div>
</div>
<div class="stk-28__card group absolute inset-0 cursor-pointer overflow-hidden rounded-3xl [transform-style:preserve-3d] [z-index:calc(4-var(--i))] brightness-[calc(1-var(--i)*0.13)] shadow-[0_30px_60px_rgba(0,0,0,0.55),inset_0_0_0_1px_rgba(255,255,255,0.08)] [transform:translateZ(calc(var(--i)*-40px))_translateY(calc(var(--i)*10px))_rotate(var(--r0))] transition-[transform,box-shadow,filter] duration-800 ease-stk28 [.stk-28__stage:hover:not(:has(.stk-28__card:hover))_&]:brightness-100 [.stk-28__stage:hover:not(:has(.stk-28__card:hover))_&]:[transform:translate(var(--fx),var(--fy))_translateZ(var(--fz))_rotate(var(--fr))] hover:z-9 hover:brightness-105 hover:shadow-[0_50px_90px_rgba(0,0,0,0.65),inset_0_0_0_1px_rgba(255,255,255,0.18),0_0_40px_rgba(255,255,255,0.12)] hover:[transform:translate(var(--fx),calc(var(--fy)-26px))_translateZ(90px)_rotate(var(--fr))_scale(1.06)] motion-reduce:transition-none" style="--i:1;--r0:2deg;--fx:-55px;--fy:18px;--fr:-4deg;--fz:20px">
<div class="absolute inset-0 backdrop-blur-[8px]" aria-hidden="true">
<div class="absolute inset-0 opacity-90 bg-[size:200%_200%] animate-stk28-flow motion-reduce:animate-none bg-linear-[135deg,var(--color-stk28-b1),var(--color-stk28-b2)_45%,var(--color-stk28-b3)]"></div>
<div class="absolute inset-0 opacity-0 mix-blend-soft-light transition-opacity duration-500 group-hover:opacity-100 [background-image:radial-gradient(280px_circle_at_50%_40%,rgba(255,255,255,0.4),transparent_60%)]"></div>
<div class="absolute -top-[60%] -left-[30%] h-[220%] w-[60%] [background-image:linear-gradient(90deg,transparent,rgba(255,255,255,0.5),transparent)] [transform:rotate(18deg)_translateX(-220px)] transition-transform duration-900 ease-stk28 group-hover:[transform:rotate(18deg)_translateX(560px)] motion-reduce:transition-none"></div>
</div>
<div class="absolute inset-0 flex flex-col justify-between p-7 [transform:translateZ(50px)]">
<div class="flex items-center gap-2.5">
<span class="rounded-full border border-white/25 bg-white/16 px-3 py-1.5 text-[0.62rem] font-medium tracking-[0.22em] uppercase text-white backdrop-blur-[6px]">Color</span>
<span class="ml-auto font-stk28-serif text-[1.3rem] italic text-white/75">02</span>
</div>
<div class="flex flex-col gap-4">
<p class="m-0 font-stk28-serif text-5xl leading-[0.92] tracking-[-0.01em] text-white [text-shadow:0_2px_20px_rgba(0,0,0,0.3)]">Warm<br><em class="italic">tones</em></p>
<p class="m-0 max-w-[24ch] text-[0.8rem] leading-relaxed text-white/80">Palette and art direction with a deliberate human warmth.</p>
<span class="inline-flex translate-y-2 items-center gap-1.5 text-[0.78rem] text-white opacity-0 transition-[opacity,transform] duration-500 delay-100 group-hover:translate-y-0 group-hover:opacity-100 motion-reduce:transition-none">View case <span class="grid size-5.5 place-items-center rounded-full bg-white/20 text-[0.8rem]" aria-hidden="true">→</span></span>
</div>
</div>
</div>
<div class="stk-28__card group absolute inset-0 cursor-pointer overflow-hidden rounded-3xl [transform-style:preserve-3d] [z-index:calc(4-var(--i))] brightness-[calc(1-var(--i)*0.13)] shadow-[0_30px_60px_rgba(0,0,0,0.55),inset_0_0_0_1px_rgba(255,255,255,0.08)] [transform:translateZ(calc(var(--i)*-40px))_translateY(calc(var(--i)*10px))_rotate(var(--r0))] transition-[transform,box-shadow,filter] duration-800 ease-stk28 [.stk-28__stage:hover:not(:has(.stk-28__card:hover))_&]:brightness-100 [.stk-28__stage:hover:not(:has(.stk-28__card:hover))_&]:[transform:translate(var(--fx),var(--fy))_translateZ(var(--fz))_rotate(var(--fr))] hover:z-9 hover:brightness-105 hover:shadow-[0_50px_90px_rgba(0,0,0,0.65),inset_0_0_0_1px_rgba(255,255,255,0.18),0_0_40px_rgba(255,255,255,0.12)] hover:[transform:translate(var(--fx),calc(var(--fy)-26px))_translateZ(90px)_rotate(var(--fr))_scale(1.06)] motion-reduce:transition-none" style="--i:2;--r0:-2deg;--fx:55px;--fy:18px;--fr:4deg;--fz:20px">
<div class="absolute inset-0 backdrop-blur-[8px]" aria-hidden="true">
<div class="absolute inset-0 opacity-90 bg-[size:200%_200%] animate-stk28-flow motion-reduce:animate-none bg-linear-[135deg,var(--color-stk28-c1),var(--color-stk28-c2)_50%,var(--color-stk28-c3)]"></div>
<div class="absolute inset-0 opacity-0 mix-blend-soft-light transition-opacity duration-500 group-hover:opacity-100 [background-image:radial-gradient(280px_circle_at_50%_40%,rgba(255,255,255,0.4),transparent_60%)]"></div>
<div class="absolute -top-[60%] -left-[30%] h-[220%] w-[60%] [background-image:linear-gradient(90deg,transparent,rgba(255,255,255,0.5),transparent)] [transform:rotate(18deg)_translateX(-220px)] transition-transform duration-900 ease-stk28 group-hover:[transform:rotate(18deg)_translateX(560px)] motion-reduce:transition-none"></div>
</div>
<div class="absolute inset-0 flex flex-col justify-between p-7 [transform:translateZ(50px)]">
<div class="flex items-center gap-2.5">
<span class="rounded-full border border-white/25 bg-white/16 px-3 py-1.5 text-[0.62rem] font-medium tracking-[0.22em] uppercase text-white backdrop-blur-[6px]">Motion</span>
<span class="ml-auto font-stk28-serif text-[1.3rem] italic text-white/75">03</span>
</div>
<div class="flex flex-col gap-4">
<p class="m-0 font-stk28-serif text-5xl leading-[0.92] tracking-[-0.01em] text-white [text-shadow:0_2px_20px_rgba(0,0,0,0.3)]">In<br><em class="italic">motion</em></p>
<p class="m-0 max-w-[24ch] text-[0.8rem] leading-relaxed text-white/80">A kinetic brand identity that moves with real intent.</p>
<span class="inline-flex translate-y-2 items-center gap-1.5 text-[0.78rem] text-white opacity-0 transition-[opacity,transform] duration-500 delay-100 group-hover:translate-y-0 group-hover:opacity-100 motion-reduce:transition-none">View case <span class="grid size-5.5 place-items-center rounded-full bg-white/20 text-[0.8rem]" aria-hidden="true">→</span></span>
</div>
</div>
</div>
<div class="stk-28__card group absolute inset-0 cursor-pointer overflow-hidden rounded-3xl [transform-style:preserve-3d] [z-index:calc(4-var(--i))] brightness-[calc(1-var(--i)*0.13)] shadow-[0_30px_60px_rgba(0,0,0,0.55),inset_0_0_0_1px_rgba(255,255,255,0.08)] [transform:translateZ(calc(var(--i)*-40px))_translateY(calc(var(--i)*10px))_rotate(var(--r0))] transition-[transform,box-shadow,filter] duration-800 ease-stk28 [.stk-28__stage:hover:not(:has(.stk-28__card:hover))_&]:brightness-100 [.stk-28__stage:hover:not(:has(.stk-28__card:hover))_&]:[transform:translate(var(--fx),var(--fy))_translateZ(var(--fz))_rotate(var(--fr))] hover:z-9 hover:brightness-105 hover:shadow-[0_50px_90px_rgba(0,0,0,0.65),inset_0_0_0_1px_rgba(255,255,255,0.18),0_0_40px_rgba(255,255,255,0.12)] hover:[transform:translate(var(--fx),calc(var(--fy)-26px))_translateZ(90px)_rotate(var(--fr))_scale(1.06)] motion-reduce:transition-none" style="--i:3;--r0:3deg;--fx:165px;--fy:-30px;--fr:12deg;--fz:40px">
<div class="absolute inset-0 backdrop-blur-[8px]" aria-hidden="true">
<div class="absolute inset-0 opacity-90 bg-[size:200%_200%] animate-stk28-flow motion-reduce:animate-none bg-linear-[135deg,var(--color-stk28-d1),var(--color-stk28-d2)_45%,var(--color-stk28-d3)]"></div>
<div class="absolute inset-0 opacity-0 mix-blend-soft-light transition-opacity duration-500 group-hover:opacity-100 [background-image:radial-gradient(280px_circle_at_50%_40%,rgba(255,255,255,0.4),transparent_60%)]"></div>
<div class="absolute -top-[60%] -left-[30%] h-[220%] w-[60%] [background-image:linear-gradient(90deg,transparent,rgba(255,255,255,0.5),transparent)] [transform:rotate(18deg)_translateX(-220px)] transition-transform duration-900 ease-stk28 group-hover:[transform:rotate(18deg)_translateX(560px)] motion-reduce:transition-none"></div>
</div>
<div class="absolute inset-0 flex flex-col justify-between p-7 [transform:translateZ(50px)]">
<div class="flex items-center gap-2.5">
<span class="rounded-full border border-white/25 bg-white/16 px-3 py-1.5 text-[0.62rem] font-medium tracking-[0.22em] uppercase text-white backdrop-blur-[6px]">Type</span>
<span class="ml-auto font-stk28-serif text-[1.3rem] italic text-white/75">04</span>
</div>
<div class="flex flex-col gap-4">
<p class="m-0 font-stk28-serif text-5xl leading-[0.92] tracking-[-0.01em] text-white [text-shadow:0_2px_20px_rgba(0,0,0,0.3)]">Letter<br><em class="italic">forms</em></p>
<p class="m-0 max-w-[24ch] text-[0.8rem] leading-relaxed text-white/80">An editorial typography system built for screens first.</p>
<span class="inline-flex translate-y-2 items-center gap-1.5 text-[0.78rem] text-white opacity-0 transition-[opacity,transform] duration-500 delay-100 group-hover:translate-y-0 group-hover:opacity-100 motion-reduce:transition-none">View case <span class="grid size-5.5 place-items-center rounded-full bg-white/20 text-[0.8rem]" aria-hidden="true">→</span></span>
</div>
</div>
</div>
<div class="absolute -bottom-[70px] left-1/2 h-10 w-60 -translate-x-1/2 blur-[8px] [background-image:radial-gradient(ellipse,rgba(0,0,0,0.6),transparent_70%)]" aria-hidden="true"></div>
</div>
</section><section class="stk-28 relative grid w-full min-h-svh place-items-center overflow-hidden bg-stk28-void font-stk28-sans">
<div class="pointer-events-none absolute -inset-[30%] blur-[20px] animate-stk28-drift motion-reduce:animate-none [background-image:radial-gradient(40%_40%_at_20%_30%,var(--color-stk28-aur1),transparent_70%),radial-gradient(45%_45%_at_80%_25%,var(--color-stk28-aur2),transparent_70%),radial-gradient(50%_50%_at_50%_85%,var(--color-stk28-aur3),transparent_70%)]" aria-hidden="true"></div>
<div class="pointer-events-none absolute inset-0 mix-blend-overlay [background-image:repeating-linear-gradient(0deg,rgba(255,255,255,0.022)_0_1px,transparent_1px_3px)]" aria-hidden="true"></div>
<div class="stk-28__stage relative z-1 h-100 w-75 [perspective:1300px]" aria-label="Selected works, four case studies">
<div class="stk-28__card group absolute inset-0 cursor-pointer overflow-hidden rounded-3xl [transform-style:preserve-3d] [z-index:calc(4-var(--i))] brightness-[calc(1-var(--i)*0.13)] shadow-[0_30px_60px_rgba(0,0,0,0.55),inset_0_0_0_1px_rgba(255,255,255,0.08)] [transform:translateZ(calc(var(--i)*-40px))_translateY(calc(var(--i)*10px))_rotate(var(--r0))] transition-[transform,box-shadow,filter] duration-800 ease-stk28 [.stk-28__stage:hover:not(:has(.stk-28__card:hover))_&]:brightness-100 [.stk-28__stage:hover:not(:has(.stk-28__card:hover))_&]:[transform:translate(var(--fx),var(--fy))_translateZ(var(--fz))_rotate(var(--fr))] hover:z-9 hover:brightness-105 hover:shadow-[0_50px_90px_rgba(0,0,0,0.65),inset_0_0_0_1px_rgba(255,255,255,0.18),0_0_40px_rgba(255,255,255,0.12)] hover:[transform:translate(var(--fx),calc(var(--fy)-26px))_translateZ(90px)_rotate(var(--fr))_scale(1.06)] motion-reduce:transition-none" style="--i:0;--r0:-3deg;--fx:-165px;--fy:-30px;--fr:-12deg;--fz:40px">
<div class="absolute inset-0 backdrop-blur-[8px]" aria-hidden="true">
<div class="absolute inset-0 opacity-90 bg-[size:200%_200%] animate-stk28-flow motion-reduce:animate-none bg-linear-[135deg,var(--color-stk28-a1),var(--color-stk28-a2)_40%,var(--color-stk28-a3)]"></div>
<div class="absolute inset-0 opacity-0 mix-blend-soft-light transition-opacity duration-500 group-hover:opacity-100 [background-image:radial-gradient(280px_circle_at_50%_40%,rgba(255,255,255,0.4),transparent_60%)]"></div>
<div class="absolute -top-[60%] -left-[30%] h-[220%] w-[60%] [background-image:linear-gradient(90deg,transparent,rgba(255,255,255,0.5),transparent)] [transform:rotate(18deg)_translateX(-220px)] transition-transform duration-900 ease-stk28 group-hover:[transform:rotate(18deg)_translateX(560px)] motion-reduce:transition-none"></div>
</div>
<div class="absolute inset-0 flex flex-col justify-between p-7 [transform:translateZ(50px)]">
<div class="flex items-center gap-2.5">
<span class="rounded-full border border-white/25 bg-white/16 px-3 py-1.5 text-[0.62rem] font-medium tracking-[0.22em] uppercase text-white backdrop-blur-[6px]">Index</span>
<span class="ml-auto font-stk28-serif text-[1.3rem] italic text-white/75">01</span>
</div>
<div class="flex flex-col gap-4">
<p class="m-0 font-stk28-serif text-5xl leading-[0.92] tracking-[-0.01em] text-white [text-shadow:0_2px_20px_rgba(0,0,0,0.3)]">Selected<br><em class="italic">works</em></p>
<p class="m-0 max-w-[24ch] text-[0.8rem] leading-relaxed text-white/80">Hover to open the deck. Move across to explore each case.</p>
<span class="inline-flex translate-y-2 items-center gap-1.5 text-[0.78rem] text-white opacity-0 transition-[opacity,transform] duration-500 delay-100 group-hover:translate-y-0 group-hover:opacity-100 motion-reduce:transition-none">Begin <span class="grid size-5.5 place-items-center rounded-full bg-white/20 text-[0.8rem]" aria-hidden="true">→</span></span>
</div>
</div>
</div>
<div class="stk-28__card group absolute inset-0 cursor-pointer overflow-hidden rounded-3xl [transform-style:preserve-3d] [z-index:calc(4-var(--i))] brightness-[calc(1-var(--i)*0.13)] shadow-[0_30px_60px_rgba(0,0,0,0.55),inset_0_0_0_1px_rgba(255,255,255,0.08)] [transform:translateZ(calc(var(--i)*-40px))_translateY(calc(var(--i)*10px))_rotate(var(--r0))] transition-[transform,box-shadow,filter] duration-800 ease-stk28 [.stk-28__stage:hover:not(:has(.stk-28__card:hover))_&]:brightness-100 [.stk-28__stage:hover:not(:has(.stk-28__card:hover))_&]:[transform:translate(var(--fx),var(--fy))_translateZ(var(--fz))_rotate(var(--fr))] hover:z-9 hover:brightness-105 hover:shadow-[0_50px_90px_rgba(0,0,0,0.65),inset_0_0_0_1px_rgba(255,255,255,0.18),0_0_40px_rgba(255,255,255,0.12)] hover:[transform:translate(var(--fx),calc(var(--fy)-26px))_translateZ(90px)_rotate(var(--fr))_scale(1.06)] motion-reduce:transition-none" style="--i:1;--r0:2deg;--fx:-55px;--fy:18px;--fr:-4deg;--fz:20px">
<div class="absolute inset-0 backdrop-blur-[8px]" aria-hidden="true">
<div class="absolute inset-0 opacity-90 bg-[size:200%_200%] animate-stk28-flow motion-reduce:animate-none bg-linear-[135deg,var(--color-stk28-b1),var(--color-stk28-b2)_45%,var(--color-stk28-b3)]"></div>
<div class="absolute inset-0 opacity-0 mix-blend-soft-light transition-opacity duration-500 group-hover:opacity-100 [background-image:radial-gradient(280px_circle_at_50%_40%,rgba(255,255,255,0.4),transparent_60%)]"></div>
<div class="absolute -top-[60%] -left-[30%] h-[220%] w-[60%] [background-image:linear-gradient(90deg,transparent,rgba(255,255,255,0.5),transparent)] [transform:rotate(18deg)_translateX(-220px)] transition-transform duration-900 ease-stk28 group-hover:[transform:rotate(18deg)_translateX(560px)] motion-reduce:transition-none"></div>
</div>
<div class="absolute inset-0 flex flex-col justify-between p-7 [transform:translateZ(50px)]">
<div class="flex items-center gap-2.5">
<span class="rounded-full border border-white/25 bg-white/16 px-3 py-1.5 text-[0.62rem] font-medium tracking-[0.22em] uppercase text-white backdrop-blur-[6px]">Color</span>
<span class="ml-auto font-stk28-serif text-[1.3rem] italic text-white/75">02</span>
</div>
<div class="flex flex-col gap-4">
<p class="m-0 font-stk28-serif text-5xl leading-[0.92] tracking-[-0.01em] text-white [text-shadow:0_2px_20px_rgba(0,0,0,0.3)]">Warm<br><em class="italic">tones</em></p>
<p class="m-0 max-w-[24ch] text-[0.8rem] leading-relaxed text-white/80">Palette and art direction with a deliberate human warmth.</p>
<span class="inline-flex translate-y-2 items-center gap-1.5 text-[0.78rem] text-white opacity-0 transition-[opacity,transform] duration-500 delay-100 group-hover:translate-y-0 group-hover:opacity-100 motion-reduce:transition-none">View case <span class="grid size-5.5 place-items-center rounded-full bg-white/20 text-[0.8rem]" aria-hidden="true">→</span></span>
</div>
</div>
</div>
<div class="stk-28__card group absolute inset-0 cursor-pointer overflow-hidden rounded-3xl [transform-style:preserve-3d] [z-index:calc(4-var(--i))] brightness-[calc(1-var(--i)*0.13)] shadow-[0_30px_60px_rgba(0,0,0,0.55),inset_0_0_0_1px_rgba(255,255,255,0.08)] [transform:translateZ(calc(var(--i)*-40px))_translateY(calc(var(--i)*10px))_rotate(var(--r0))] transition-[transform,box-shadow,filter] duration-800 ease-stk28 [.stk-28__stage:hover:not(:has(.stk-28__card:hover))_&]:brightness-100 [.stk-28__stage:hover:not(:has(.stk-28__card:hover))_&]:[transform:translate(var(--fx),var(--fy))_translateZ(var(--fz))_rotate(var(--fr))] hover:z-9 hover:brightness-105 hover:shadow-[0_50px_90px_rgba(0,0,0,0.65),inset_0_0_0_1px_rgba(255,255,255,0.18),0_0_40px_rgba(255,255,255,0.12)] hover:[transform:translate(var(--fx),calc(var(--fy)-26px))_translateZ(90px)_rotate(var(--fr))_scale(1.06)] motion-reduce:transition-none" style="--i:2;--r0:-2deg;--fx:55px;--fy:18px;--fr:4deg;--fz:20px">
<div class="absolute inset-0 backdrop-blur-[8px]" aria-hidden="true">
<div class="absolute inset-0 opacity-90 bg-[size:200%_200%] animate-stk28-flow motion-reduce:animate-none bg-linear-[135deg,var(--color-stk28-c1),var(--color-stk28-c2)_50%,var(--color-stk28-c3)]"></div>
<div class="absolute inset-0 opacity-0 mix-blend-soft-light transition-opacity duration-500 group-hover:opacity-100 [background-image:radial-gradient(280px_circle_at_50%_40%,rgba(255,255,255,0.4),transparent_60%)]"></div>
<div class="absolute -top-[60%] -left-[30%] h-[220%] w-[60%] [background-image:linear-gradient(90deg,transparent,rgba(255,255,255,0.5),transparent)] [transform:rotate(18deg)_translateX(-220px)] transition-transform duration-900 ease-stk28 group-hover:[transform:rotate(18deg)_translateX(560px)] motion-reduce:transition-none"></div>
</div>
<div class="absolute inset-0 flex flex-col justify-between p-7 [transform:translateZ(50px)]">
<div class="flex items-center gap-2.5">
<span class="rounded-full border border-white/25 bg-white/16 px-3 py-1.5 text-[0.62rem] font-medium tracking-[0.22em] uppercase text-white backdrop-blur-[6px]">Motion</span>
<span class="ml-auto font-stk28-serif text-[1.3rem] italic text-white/75">03</span>
</div>
<div class="flex flex-col gap-4">
<p class="m-0 font-stk28-serif text-5xl leading-[0.92] tracking-[-0.01em] text-white [text-shadow:0_2px_20px_rgba(0,0,0,0.3)]">In<br><em class="italic">motion</em></p>
<p class="m-0 max-w-[24ch] text-[0.8rem] leading-relaxed text-white/80">A kinetic brand identity that moves with real intent.</p>
<span class="inline-flex translate-y-2 items-center gap-1.5 text-[0.78rem] text-white opacity-0 transition-[opacity,transform] duration-500 delay-100 group-hover:translate-y-0 group-hover:opacity-100 motion-reduce:transition-none">View case <span class="grid size-5.5 place-items-center rounded-full bg-white/20 text-[0.8rem]" aria-hidden="true">→</span></span>
</div>
</div>
</div>
<div class="stk-28__card group absolute inset-0 cursor-pointer overflow-hidden rounded-3xl [transform-style:preserve-3d] [z-index:calc(4-var(--i))] brightness-[calc(1-var(--i)*0.13)] shadow-[0_30px_60px_rgba(0,0,0,0.55),inset_0_0_0_1px_rgba(255,255,255,0.08)] [transform:translateZ(calc(var(--i)*-40px))_translateY(calc(var(--i)*10px))_rotate(var(--r0))] transition-[transform,box-shadow,filter] duration-800 ease-stk28 [.stk-28__stage:hover:not(:has(.stk-28__card:hover))_&]:brightness-100 [.stk-28__stage:hover:not(:has(.stk-28__card:hover))_&]:[transform:translate(var(--fx),var(--fy))_translateZ(var(--fz))_rotate(var(--fr))] hover:z-9 hover:brightness-105 hover:shadow-[0_50px_90px_rgba(0,0,0,0.65),inset_0_0_0_1px_rgba(255,255,255,0.18),0_0_40px_rgba(255,255,255,0.12)] hover:[transform:translate(var(--fx),calc(var(--fy)-26px))_translateZ(90px)_rotate(var(--fr))_scale(1.06)] motion-reduce:transition-none" style="--i:3;--r0:3deg;--fx:165px;--fy:-30px;--fr:12deg;--fz:40px">
<div class="absolute inset-0 backdrop-blur-[8px]" aria-hidden="true">
<div class="absolute inset-0 opacity-90 bg-[size:200%_200%] animate-stk28-flow motion-reduce:animate-none bg-linear-[135deg,var(--color-stk28-d1),var(--color-stk28-d2)_45%,var(--color-stk28-d3)]"></div>
<div class="absolute inset-0 opacity-0 mix-blend-soft-light transition-opacity duration-500 group-hover:opacity-100 [background-image:radial-gradient(280px_circle_at_50%_40%,rgba(255,255,255,0.4),transparent_60%)]"></div>
<div class="absolute -top-[60%] -left-[30%] h-[220%] w-[60%] [background-image:linear-gradient(90deg,transparent,rgba(255,255,255,0.5),transparent)] [transform:rotate(18deg)_translateX(-220px)] transition-transform duration-900 ease-stk28 group-hover:[transform:rotate(18deg)_translateX(560px)] motion-reduce:transition-none"></div>
</div>
<div class="absolute inset-0 flex flex-col justify-between p-7 [transform:translateZ(50px)]">
<div class="flex items-center gap-2.5">
<span class="rounded-full border border-white/25 bg-white/16 px-3 py-1.5 text-[0.62rem] font-medium tracking-[0.22em] uppercase text-white backdrop-blur-[6px]">Type</span>
<span class="ml-auto font-stk28-serif text-[1.3rem] italic text-white/75">04</span>
</div>
<div class="flex flex-col gap-4">
<p class="m-0 font-stk28-serif text-5xl leading-[0.92] tracking-[-0.01em] text-white [text-shadow:0_2px_20px_rgba(0,0,0,0.3)]">Letter<br><em class="italic">forms</em></p>
<p class="m-0 max-w-[24ch] text-[0.8rem] leading-relaxed text-white/80">An editorial typography system built for screens first.</p>
<span class="inline-flex translate-y-2 items-center gap-1.5 text-[0.78rem] text-white opacity-0 transition-[opacity,transform] duration-500 delay-100 group-hover:translate-y-0 group-hover:opacity-100 motion-reduce:transition-none">View case <span class="grid size-5.5 place-items-center rounded-full bg-white/20 text-[0.8rem]" aria-hidden="true">→</span></span>
</div>
</div>
</div>
<div class="absolute -bottom-[70px] left-1/2 h-10 w-60 -translate-x-1/2 blur-[8px] [background-image:radial-gradient(ellipse,rgba(0,0,0,0.6),transparent_70%)]" aria-hidden="true"></div>
</div>
</section>@import "tailwindcss";
@theme {
--color-stk28-void: #05060a;
--color-stk28-void: oklch(0.08 0.012 275);
--color-stk28-aur1: rgba(99, 102, 241, 0.35);
--color-stk28-aur2: rgba(236, 72, 153, 0.30);
--color-stk28-aur3: rgba(45, 212, 191, 0.28);
--color-stk28-a1: #1e1b4b;
--color-stk28-a2: #4338ca;
--color-stk28-a3: #6d28d9;
--color-stk28-b1: #831843;
--color-stk28-b2: #be185d;
--color-stk28-b3: #f43f5e;
--color-stk28-c1: #064e3b;
--color-stk28-c2: #0d9488;
--color-stk28-c3: #2dd4bf;
--color-stk28-d1: #7c2d12;
--color-stk28-d2: #ea580c;
--color-stk28-d3: #f59e0b;
--font-stk28-sans: 'Geist', system-ui, sans-serif;
--font-stk28-serif: 'Instrument Serif', Georgia, serif;
--ease-stk28: cubic-bezier(0.16, 1, 0.3, 1);
--animate-stk28-flow: stk-28-flow 8s ease infinite;
--animate-stk28-drift: stk-28-drift 18s ease-in-out infinite alternate;
}
@keyframes stk-28-flow {
0%,
100% {
background-position: 0% 50%;
}
50% {
background-position: 100% 50%;
}
}
@keyframes stk-28-drift {
from {
transform: translate(-3%, -2%) scale(1);
}
to {
transform: translate(4%, 3%) scale(1.12);
}
}@import "tailwindcss";
@theme {
--color-stk28-void: #05060a;
--color-stk28-void: oklch(0.08 0.012 275);
--color-stk28-aur1: rgba(99, 102, 241, 0.35);
--color-stk28-aur2: rgba(236, 72, 153, 0.30);
--color-stk28-aur3: rgba(45, 212, 191, 0.28);
--color-stk28-a1: #1e1b4b;
--color-stk28-a2: #4338ca;
--color-stk28-a3: #6d28d9;
--color-stk28-b1: #831843;
--color-stk28-b2: #be185d;
--color-stk28-b3: #f43f5e;
--color-stk28-c1: #064e3b;
--color-stk28-c2: #0d9488;
--color-stk28-c3: #2dd4bf;
--color-stk28-d1: #7c2d12;
--color-stk28-d2: #ea580c;
--color-stk28-d3: #f59e0b;
--font-stk28-sans: 'Geist', system-ui, sans-serif;
--font-stk28-serif: 'Instrument Serif', Georgia, serif;
--ease-stk28: cubic-bezier(0.16, 1, 0.3, 1);
--animate-stk28-flow: stk-28-flow 8s ease infinite;
--animate-stk28-drift: stk-28-drift 18s ease-in-out infinite alternate;
}
@keyframes stk-28-flow {
0%,
100% {
background-position: 0% 50%;
}
50% {
background-position: 100% 50%;
}
}
@keyframes stk-28-drift {
from {
transform: translate(-3%, -2%) scale(1);
}
to {
transform: translate(4%, 3%) scale(1.12);
}
}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-stk28-void: #05060a;
--color-stk28-void: oklch(0.08 0.012 275);
--color-stk28-aur1: rgba(99, 102, 241, 0.35);
--color-stk28-aur2: rgba(236, 72, 153, 0.30);
--color-stk28-aur3: rgba(45, 212, 191, 0.28);
--color-stk28-a1: #1e1b4b;
--color-stk28-a2: #4338ca;
--color-stk28-a3: #6d28d9;
--color-stk28-b1: #831843;
--color-stk28-b2: #be185d;
--color-stk28-b3: #f43f5e;
--color-stk28-c1: #064e3b;
--color-stk28-c2: #0d9488;
--color-stk28-c3: #2dd4bf;
--color-stk28-d1: #7c2d12;
--color-stk28-d2: #ea580c;
--color-stk28-d3: #f59e0b;
--font-stk28-sans: 'Geist', system-ui, sans-serif;
--font-stk28-serif: 'Instrument Serif', Georgia, serif;
--ease-stk28: cubic-bezier(0.16, 1, 0.3, 1);
--animate-stk28-flow: stk-28-flow 8s ease infinite;
--animate-stk28-drift: stk-28-drift 18s ease-in-out infinite alternate;
}
@keyframes stk-28-flow {
0%,
100% {
background-position: 0% 50%;
}
50% {
background-position: 100% 50%;
}
}
@keyframes stk-28-drift {
from {
transform: translate(-3%, -2%) scale(1);
}
to {
transform: translate(4%, 3%) scale(1.12);
}
}@import "tailwindcss";
@theme {
--color-stk28-void: #05060a;
--color-stk28-void: oklch(0.08 0.012 275);
--color-stk28-aur1: rgba(99, 102, 241, 0.35);
--color-stk28-aur2: rgba(236, 72, 153, 0.30);
--color-stk28-aur3: rgba(45, 212, 191, 0.28);
--color-stk28-a1: #1e1b4b;
--color-stk28-a2: #4338ca;
--color-stk28-a3: #6d28d9;
--color-stk28-b1: #831843;
--color-stk28-b2: #be185d;
--color-stk28-b3: #f43f5e;
--color-stk28-c1: #064e3b;
--color-stk28-c2: #0d9488;
--color-stk28-c3: #2dd4bf;
--color-stk28-d1: #7c2d12;
--color-stk28-d2: #ea580c;
--color-stk28-d3: #f59e0b;
--font-stk28-sans: 'Geist', system-ui, sans-serif;
--font-stk28-serif: 'Instrument Serif', Georgia, serif;
--ease-stk28: cubic-bezier(0.16, 1, 0.3, 1);
--animate-stk28-flow: stk-28-flow 8s ease infinite;
--animate-stk28-drift: stk-28-drift 18s ease-in-out infinite alternate;
}
@keyframes stk-28-flow {
0%,
100% {
background-position: 0% 50%;
}
50% {
background-position: 100% 50%;
}
}
@keyframes stk-28-drift {
from {
transform: translate(-3%, -2%) scale(1);
}
to {
transform: translate(4%, 3%) scale(1.12);
}
}<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=Instrument+Serif:ital@0;1&family=Geist:wght@300;400;500;600&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=Instrument+Serif:ital@0;1&family=Geist:wght@300;400;500;600&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. - The flicker guard is the whole port:
[.stk-28__stage:hover:not(:has(.stk-28__card:hover))_&]:says ‘my stage is hovered and nothing inside it is’, which no composition of built-in variants can say. - Moving the fan destinations into
--fx/--fy/--fr/--fzmakes all four cards byte-identical class lists — the practical answer to utility repetition is properties, not more classes. brightness-[calc(1-var(--i)*0.13)]turns four filter rules into one depth-derived class, and the fan restores every card to full colour.- The hover lift keeps the card's fanned
--fxrather than returning to the origin, which is the other half of the anti-flicker fix. - Two display faces arrive as
--font-*tokens, so the serif isfont-stk28-serifrather than a rule per heading. - Three decorative animations — the aurora drift, the gradient flow and the sheen sweep — each carry their own
motion-reduce:off-switch rather than sharing one media block.
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 Hover Reveal Spread
Source: https://codefronts.com/components/tailwind-stacked-cards/tailwind-stacked-cards-hover-reveal-spread/
The editorial portfolio deck: four gradient cards in a slight pile that fan into an arc when you approach, then lift individually. The port's real subject is the flicker guard — the fan must apply on stage hover except when a card itself is hovered, and no built-in variant expresses that, so it becomes one honest arbitrary variant. All four cards then share a single identical class list, with their destinations as custom properties.
## HTML
```html
<section class="stk-28 relative grid w-full min-h-svh place-items-center overflow-hidden bg-stk28-void font-stk28-sans">
<div class="pointer-events-none absolute -inset-[30%] blur-[20px] animate-stk28-drift motion-reduce:animate-none [background-image:radial-gradient(40%_40%_at_20%_30%,var(--color-stk28-aur1),transparent_70%),radial-gradient(45%_45%_at_80%_25%,var(--color-stk28-aur2),transparent_70%),radial-gradient(50%_50%_at_50%_85%,var(--color-stk28-aur3),transparent_70%)]" aria-hidden="true"></div>
<div class="pointer-events-none absolute inset-0 mix-blend-overlay [background-image:repeating-linear-gradient(0deg,rgba(255,255,255,0.022)_0_1px,transparent_1px_3px)]" aria-hidden="true"></div>
<div class="stk-28__stage relative z-1 h-100 w-75 [perspective:1300px]" aria-label="Selected works, four case studies">
<div class="stk-28__card group absolute inset-0 cursor-pointer overflow-hidden rounded-3xl [transform-style:preserve-3d] [z-index:calc(4-var(--i))] brightness-[calc(1-var(--i)*0.13)] shadow-[0_30px_60px_rgba(0,0,0,0.55),inset_0_0_0_1px_rgba(255,255,255,0.08)] [transform:translateZ(calc(var(--i)*-40px))_translateY(calc(var(--i)*10px))_rotate(var(--r0))] transition-[transform,box-shadow,filter] duration-800 ease-stk28 [.stk-28__stage:hover:not(:has(.stk-28__card:hover))_&]:brightness-100 [.stk-28__stage:hover:not(:has(.stk-28__card:hover))_&]:[transform:translate(var(--fx),var(--fy))_translateZ(var(--fz))_rotate(var(--fr))] hover:z-9 hover:brightness-105 hover:shadow-[0_50px_90px_rgba(0,0,0,0.65),inset_0_0_0_1px_rgba(255,255,255,0.18),0_0_40px_rgba(255,255,255,0.12)] hover:[transform:translate(var(--fx),calc(var(--fy)-26px))_translateZ(90px)_rotate(var(--fr))_scale(1.06)] motion-reduce:transition-none" style="--i:0;--r0:-3deg;--fx:-165px;--fy:-30px;--fr:-12deg;--fz:40px">
<div class="absolute inset-0 backdrop-blur-[8px]" aria-hidden="true">
<div class="absolute inset-0 opacity-90 bg-[size:200%_200%] animate-stk28-flow motion-reduce:animate-none bg-linear-[135deg,var(--color-stk28-a1),var(--color-stk28-a2)_40%,var(--color-stk28-a3)]"></div>
<div class="absolute inset-0 opacity-0 mix-blend-soft-light transition-opacity duration-500 group-hover:opacity-100 [background-image:radial-gradient(280px_circle_at_50%_40%,rgba(255,255,255,0.4),transparent_60%)]"></div>
<div class="absolute -top-[60%] -left-[30%] h-[220%] w-[60%] [background-image:linear-gradient(90deg,transparent,rgba(255,255,255,0.5),transparent)] [transform:rotate(18deg)_translateX(-220px)] transition-transform duration-900 ease-stk28 group-hover:[transform:rotate(18deg)_translateX(560px)] motion-reduce:transition-none"></div>
</div>
<div class="absolute inset-0 flex flex-col justify-between p-7 [transform:translateZ(50px)]">
<div class="flex items-center gap-2.5">
<span class="rounded-full border border-white/25 bg-white/16 px-3 py-1.5 text-[0.62rem] font-medium tracking-[0.22em] uppercase text-white backdrop-blur-[6px]">Index</span>
<span class="ml-auto font-stk28-serif text-[1.3rem] italic text-white/75">01</span>
</div>
<div class="flex flex-col gap-4">
<p class="m-0 font-stk28-serif text-5xl leading-[0.92] tracking-[-0.01em] text-white [text-shadow:0_2px_20px_rgba(0,0,0,0.3)]">Selected<br><em class="italic">works</em></p>
<p class="m-0 max-w-[24ch] text-[0.8rem] leading-relaxed text-white/80">Hover to open the deck. Move across to explore each case.</p>
<span class="inline-flex translate-y-2 items-center gap-1.5 text-[0.78rem] text-white opacity-0 transition-[opacity,transform] duration-500 delay-100 group-hover:translate-y-0 group-hover:opacity-100 motion-reduce:transition-none">Begin <span class="grid size-5.5 place-items-center rounded-full bg-white/20 text-[0.8rem]" aria-hidden="true">→</span></span>
</div>
</div>
</div>
<div class="stk-28__card group absolute inset-0 cursor-pointer overflow-hidden rounded-3xl [transform-style:preserve-3d] [z-index:calc(4-var(--i))] brightness-[calc(1-var(--i)*0.13)] shadow-[0_30px_60px_rgba(0,0,0,0.55),inset_0_0_0_1px_rgba(255,255,255,0.08)] [transform:translateZ(calc(var(--i)*-40px))_translateY(calc(var(--i)*10px))_rotate(var(--r0))] transition-[transform,box-shadow,filter] duration-800 ease-stk28 [.stk-28__stage:hover:not(:has(.stk-28__card:hover))_&]:brightness-100 [.stk-28__stage:hover:not(:has(.stk-28__card:hover))_&]:[transform:translate(var(--fx),var(--fy))_translateZ(var(--fz))_rotate(var(--fr))] hover:z-9 hover:brightness-105 hover:shadow-[0_50px_90px_rgba(0,0,0,0.65),inset_0_0_0_1px_rgba(255,255,255,0.18),0_0_40px_rgba(255,255,255,0.12)] hover:[transform:translate(var(--fx),calc(var(--fy)-26px))_translateZ(90px)_rotate(var(--fr))_scale(1.06)] motion-reduce:transition-none" style="--i:1;--r0:2deg;--fx:-55px;--fy:18px;--fr:-4deg;--fz:20px">
<div class="absolute inset-0 backdrop-blur-[8px]" aria-hidden="true">
<div class="absolute inset-0 opacity-90 bg-[size:200%_200%] animate-stk28-flow motion-reduce:animate-none bg-linear-[135deg,var(--color-stk28-b1),var(--color-stk28-b2)_45%,var(--color-stk28-b3)]"></div>
<div class="absolute inset-0 opacity-0 mix-blend-soft-light transition-opacity duration-500 group-hover:opacity-100 [background-image:radial-gradient(280px_circle_at_50%_40%,rgba(255,255,255,0.4),transparent_60%)]"></div>
<div class="absolute -top-[60%] -left-[30%] h-[220%] w-[60%] [background-image:linear-gradient(90deg,transparent,rgba(255,255,255,0.5),transparent)] [transform:rotate(18deg)_translateX(-220px)] transition-transform duration-900 ease-stk28 group-hover:[transform:rotate(18deg)_translateX(560px)] motion-reduce:transition-none"></div>
</div>
<div class="absolute inset-0 flex flex-col justify-between p-7 [transform:translateZ(50px)]">
<div class="flex items-center gap-2.5">
<span class="rounded-full border border-white/25 bg-white/16 px-3 py-1.5 text-[0.62rem] font-medium tracking-[0.22em] uppercase text-white backdrop-blur-[6px]">Color</span>
<span class="ml-auto font-stk28-serif text-[1.3rem] italic text-white/75">02</span>
</div>
<div class="flex flex-col gap-4">
<p class="m-0 font-stk28-serif text-5xl leading-[0.92] tracking-[-0.01em] text-white [text-shadow:0_2px_20px_rgba(0,0,0,0.3)]">Warm<br><em class="italic">tones</em></p>
<p class="m-0 max-w-[24ch] text-[0.8rem] leading-relaxed text-white/80">Palette and art direction with a deliberate human warmth.</p>
<span class="inline-flex translate-y-2 items-center gap-1.5 text-[0.78rem] text-white opacity-0 transition-[opacity,transform] duration-500 delay-100 group-hover:translate-y-0 group-hover:opacity-100 motion-reduce:transition-none">View case <span class="grid size-5.5 place-items-center rounded-full bg-white/20 text-[0.8rem]" aria-hidden="true">→</span></span>
</div>
</div>
</div>
<div class="stk-28__card group absolute inset-0 cursor-pointer overflow-hidden rounded-3xl [transform-style:preserve-3d] [z-index:calc(4-var(--i))] brightness-[calc(1-var(--i)*0.13)] shadow-[0_30px_60px_rgba(0,0,0,0.55),inset_0_0_0_1px_rgba(255,255,255,0.08)] [transform:translateZ(calc(var(--i)*-40px))_translateY(calc(var(--i)*10px))_rotate(var(--r0))] transition-[transform,box-shadow,filter] duration-800 ease-stk28 [.stk-28__stage:hover:not(:has(.stk-28__card:hover))_&]:brightness-100 [.stk-28__stage:hover:not(:has(.stk-28__card:hover))_&]:[transform:translate(var(--fx),var(--fy))_translateZ(var(--fz))_rotate(var(--fr))] hover:z-9 hover:brightness-105 hover:shadow-[0_50px_90px_rgba(0,0,0,0.65),inset_0_0_0_1px_rgba(255,255,255,0.18),0_0_40px_rgba(255,255,255,0.12)] hover:[transform:translate(var(--fx),calc(var(--fy)-26px))_translateZ(90px)_rotate(var(--fr))_scale(1.06)] motion-reduce:transition-none" style="--i:2;--r0:-2deg;--fx:55px;--fy:18px;--fr:4deg;--fz:20px">
<div class="absolute inset-0 backdrop-blur-[8px]" aria-hidden="true">
<div class="absolute inset-0 opacity-90 bg-[size:200%_200%] animate-stk28-flow motion-reduce:animate-none bg-linear-[135deg,var(--color-stk28-c1),var(--color-stk28-c2)_50%,var(--color-stk28-c3)]"></div>
<div class="absolute inset-0 opacity-0 mix-blend-soft-light transition-opacity duration-500 group-hover:opacity-100 [background-image:radial-gradient(280px_circle_at_50%_40%,rgba(255,255,255,0.4),transparent_60%)]"></div>
<div class="absolute -top-[60%] -left-[30%] h-[220%] w-[60%] [background-image:linear-gradient(90deg,transparent,rgba(255,255,255,0.5),transparent)] [transform:rotate(18deg)_translateX(-220px)] transition-transform duration-900 ease-stk28 group-hover:[transform:rotate(18deg)_translateX(560px)] motion-reduce:transition-none"></div>
</div>
<div class="absolute inset-0 flex flex-col justify-between p-7 [transform:translateZ(50px)]">
<div class="flex items-center gap-2.5">
<span class="rounded-full border border-white/25 bg-white/16 px-3 py-1.5 text-[0.62rem] font-medium tracking-[0.22em] uppercase text-white backdrop-blur-[6px]">Motion</span>
<span class="ml-auto font-stk28-serif text-[1.3rem] italic text-white/75">03</span>
</div>
<div class="flex flex-col gap-4">
<p class="m-0 font-stk28-serif text-5xl leading-[0.92] tracking-[-0.01em] text-white [text-shadow:0_2px_20px_rgba(0,0,0,0.3)]">In<br><em class="italic">motion</em></p>
<p class="m-0 max-w-[24ch] text-[0.8rem] leading-relaxed text-white/80">A kinetic brand identity that moves with real intent.</p>
<span class="inline-flex translate-y-2 items-center gap-1.5 text-[0.78rem] text-white opacity-0 transition-[opacity,transform] duration-500 delay-100 group-hover:translate-y-0 group-hover:opacity-100 motion-reduce:transition-none">View case <span class="grid size-5.5 place-items-center rounded-full bg-white/20 text-[0.8rem]" aria-hidden="true">→</span></span>
</div>
</div>
</div>
<div class="stk-28__card group absolute inset-0 cursor-pointer overflow-hidden rounded-3xl [transform-style:preserve-3d] [z-index:calc(4-var(--i))] brightness-[calc(1-var(--i)*0.13)] shadow-[0_30px_60px_rgba(0,0,0,0.55),inset_0_0_0_1px_rgba(255,255,255,0.08)] [transform:translateZ(calc(var(--i)*-40px))_translateY(calc(var(--i)*10px))_rotate(var(--r0))] transition-[transform,box-shadow,filter] duration-800 ease-stk28 [.stk-28__stage:hover:not(:has(.stk-28__card:hover))_&]:brightness-100 [.stk-28__stage:hover:not(:has(.stk-28__card:hover))_&]:[transform:translate(var(--fx),var(--fy))_translateZ(var(--fz))_rotate(var(--fr))] hover:z-9 hover:brightness-105 hover:shadow-[0_50px_90px_rgba(0,0,0,0.65),inset_0_0_0_1px_rgba(255,255,255,0.18),0_0_40px_rgba(255,255,255,0.12)] hover:[transform:translate(var(--fx),calc(var(--fy)-26px))_translateZ(90px)_rotate(var(--fr))_scale(1.06)] motion-reduce:transition-none" style="--i:3;--r0:3deg;--fx:165px;--fy:-30px;--fr:12deg;--fz:40px">
<div class="absolute inset-0 backdrop-blur-[8px]" aria-hidden="true">
<div class="absolute inset-0 opacity-90 bg-[size:200%_200%] animate-stk28-flow motion-reduce:animate-none bg-linear-[135deg,var(--color-stk28-d1),var(--color-stk28-d2)_45%,var(--color-stk28-d3)]"></div>
<div class="absolute inset-0 opacity-0 mix-blend-soft-light transition-opacity duration-500 group-hover:opacity-100 [background-image:radial-gradient(280px_circle_at_50%_40%,rgba(255,255,255,0.4),transparent_60%)]"></div>
<div class="absolute -top-[60%] -left-[30%] h-[220%] w-[60%] [background-image:linear-gradient(90deg,transparent,rgba(255,255,255,0.5),transparent)] [transform:rotate(18deg)_translateX(-220px)] transition-transform duration-900 ease-stk28 group-hover:[transform:rotate(18deg)_translateX(560px)] motion-reduce:transition-none"></div>
</div>
<div class="absolute inset-0 flex flex-col justify-between p-7 [transform:translateZ(50px)]">
<div class="flex items-center gap-2.5">
<span class="rounded-full border border-white/25 bg-white/16 px-3 py-1.5 text-[0.62rem] font-medium tracking-[0.22em] uppercase text-white backdrop-blur-[6px]">Type</span>
<span class="ml-auto font-stk28-serif text-[1.3rem] italic text-white/75">04</span>
</div>
<div class="flex flex-col gap-4">
<p class="m-0 font-stk28-serif text-5xl leading-[0.92] tracking-[-0.01em] text-white [text-shadow:0_2px_20px_rgba(0,0,0,0.3)]">Letter<br><em class="italic">forms</em></p>
<p class="m-0 max-w-[24ch] text-[0.8rem] leading-relaxed text-white/80">An editorial typography system built for screens first.</p>
<span class="inline-flex translate-y-2 items-center gap-1.5 text-[0.78rem] text-white opacity-0 transition-[opacity,transform] duration-500 delay-100 group-hover:translate-y-0 group-hover:opacity-100 motion-reduce:transition-none">View case <span class="grid size-5.5 place-items-center rounded-full bg-white/20 text-[0.8rem]" aria-hidden="true">→</span></span>
</div>
</div>
</div>
<div class="absolute -bottom-[70px] left-1/2 h-10 w-60 -translate-x-1/2 blur-[8px] [background-image:radial-gradient(ellipse,rgba(0,0,0,0.6),transparent_70%)]" aria-hidden="true"></div>
</div>
</section>
```
## CSS
```css
@import "tailwindcss";
@theme {
--color-stk28-void: #05060a;
--color-stk28-void: oklch(0.08 0.012 275);
--color-stk28-aur1: rgba(99, 102, 241, 0.35);
--color-stk28-aur2: rgba(236, 72, 153, 0.30);
--color-stk28-aur3: rgba(45, 212, 191, 0.28);
--color-stk28-a1: #1e1b4b;
--color-stk28-a2: #4338ca;
--color-stk28-a3: #6d28d9;
--color-stk28-b1: #831843;
--color-stk28-b2: #be185d;
--color-stk28-b3: #f43f5e;
--color-stk28-c1: #064e3b;
--color-stk28-c2: #0d9488;
--color-stk28-c3: #2dd4bf;
--color-stk28-d1: #7c2d12;
--color-stk28-d2: #ea580c;
--color-stk28-d3: #f59e0b;
--font-stk28-sans: 'Geist', system-ui, sans-serif;
--font-stk28-serif: 'Instrument Serif', Georgia, serif;
--ease-stk28: cubic-bezier(0.16, 1, 0.3, 1);
--animate-stk28-flow: stk-28-flow 8s ease infinite;
--animate-stk28-drift: stk-28-drift 18s ease-in-out infinite alternate;
}
@keyframes stk-28-flow {
0%,
100% {
background-position: 0% 50%;
}
50% {
background-position: 100% 50%;
}
}
@keyframes stk-28-drift {
from {
transform: translate(-3%, -2%) scale(1);
}
to {
transform: translate(4%, 3%) scale(1.12);
}
}
```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 Hover Reveal Spread
Source: https://codefronts.com/components/tailwind-stacked-cards/tailwind-stacked-cards-hover-reveal-spread/
The editorial portfolio deck: four gradient cards in a slight pile that fan into an arc when you approach, then lift individually. The port's real subject is the flicker guard — the fan must apply on stage hover except when a card itself is hovered, and no built-in variant expresses that, so it becomes one honest arbitrary variant. All four cards then share a single identical class list, with their destinations as custom properties.
## HTML
```html
<section class="stk-28 relative grid w-full min-h-svh place-items-center overflow-hidden bg-stk28-void font-stk28-sans">
<div class="pointer-events-none absolute -inset-[30%] blur-[20px] animate-stk28-drift motion-reduce:animate-none [background-image:radial-gradient(40%_40%_at_20%_30%,var(--color-stk28-aur1),transparent_70%),radial-gradient(45%_45%_at_80%_25%,var(--color-stk28-aur2),transparent_70%),radial-gradient(50%_50%_at_50%_85%,var(--color-stk28-aur3),transparent_70%)]" aria-hidden="true"></div>
<div class="pointer-events-none absolute inset-0 mix-blend-overlay [background-image:repeating-linear-gradient(0deg,rgba(255,255,255,0.022)_0_1px,transparent_1px_3px)]" aria-hidden="true"></div>
<div class="stk-28__stage relative z-1 h-100 w-75 [perspective:1300px]" aria-label="Selected works, four case studies">
<div class="stk-28__card group absolute inset-0 cursor-pointer overflow-hidden rounded-3xl [transform-style:preserve-3d] [z-index:calc(4-var(--i))] brightness-[calc(1-var(--i)*0.13)] shadow-[0_30px_60px_rgba(0,0,0,0.55),inset_0_0_0_1px_rgba(255,255,255,0.08)] [transform:translateZ(calc(var(--i)*-40px))_translateY(calc(var(--i)*10px))_rotate(var(--r0))] transition-[transform,box-shadow,filter] duration-800 ease-stk28 [.stk-28__stage:hover:not(:has(.stk-28__card:hover))_&]:brightness-100 [.stk-28__stage:hover:not(:has(.stk-28__card:hover))_&]:[transform:translate(var(--fx),var(--fy))_translateZ(var(--fz))_rotate(var(--fr))] hover:z-9 hover:brightness-105 hover:shadow-[0_50px_90px_rgba(0,0,0,0.65),inset_0_0_0_1px_rgba(255,255,255,0.18),0_0_40px_rgba(255,255,255,0.12)] hover:[transform:translate(var(--fx),calc(var(--fy)-26px))_translateZ(90px)_rotate(var(--fr))_scale(1.06)] motion-reduce:transition-none" style="--i:0;--r0:-3deg;--fx:-165px;--fy:-30px;--fr:-12deg;--fz:40px">
<div class="absolute inset-0 backdrop-blur-[8px]" aria-hidden="true">
<div class="absolute inset-0 opacity-90 bg-[size:200%_200%] animate-stk28-flow motion-reduce:animate-none bg-linear-[135deg,var(--color-stk28-a1),var(--color-stk28-a2)_40%,var(--color-stk28-a3)]"></div>
<div class="absolute inset-0 opacity-0 mix-blend-soft-light transition-opacity duration-500 group-hover:opacity-100 [background-image:radial-gradient(280px_circle_at_50%_40%,rgba(255,255,255,0.4),transparent_60%)]"></div>
<div class="absolute -top-[60%] -left-[30%] h-[220%] w-[60%] [background-image:linear-gradient(90deg,transparent,rgba(255,255,255,0.5),transparent)] [transform:rotate(18deg)_translateX(-220px)] transition-transform duration-900 ease-stk28 group-hover:[transform:rotate(18deg)_translateX(560px)] motion-reduce:transition-none"></div>
</div>
<div class="absolute inset-0 flex flex-col justify-between p-7 [transform:translateZ(50px)]">
<div class="flex items-center gap-2.5">
<span class="rounded-full border border-white/25 bg-white/16 px-3 py-1.5 text-[0.62rem] font-medium tracking-[0.22em] uppercase text-white backdrop-blur-[6px]">Index</span>
<span class="ml-auto font-stk28-serif text-[1.3rem] italic text-white/75">01</span>
</div>
<div class="flex flex-col gap-4">
<p class="m-0 font-stk28-serif text-5xl leading-[0.92] tracking-[-0.01em] text-white [text-shadow:0_2px_20px_rgba(0,0,0,0.3)]">Selected<br><em class="italic">works</em></p>
<p class="m-0 max-w-[24ch] text-[0.8rem] leading-relaxed text-white/80">Hover to open the deck. Move across to explore each case.</p>
<span class="inline-flex translate-y-2 items-center gap-1.5 text-[0.78rem] text-white opacity-0 transition-[opacity,transform] duration-500 delay-100 group-hover:translate-y-0 group-hover:opacity-100 motion-reduce:transition-none">Begin <span class="grid size-5.5 place-items-center rounded-full bg-white/20 text-[0.8rem]" aria-hidden="true">→</span></span>
</div>
</div>
</div>
<div class="stk-28__card group absolute inset-0 cursor-pointer overflow-hidden rounded-3xl [transform-style:preserve-3d] [z-index:calc(4-var(--i))] brightness-[calc(1-var(--i)*0.13)] shadow-[0_30px_60px_rgba(0,0,0,0.55),inset_0_0_0_1px_rgba(255,255,255,0.08)] [transform:translateZ(calc(var(--i)*-40px))_translateY(calc(var(--i)*10px))_rotate(var(--r0))] transition-[transform,box-shadow,filter] duration-800 ease-stk28 [.stk-28__stage:hover:not(:has(.stk-28__card:hover))_&]:brightness-100 [.stk-28__stage:hover:not(:has(.stk-28__card:hover))_&]:[transform:translate(var(--fx),var(--fy))_translateZ(var(--fz))_rotate(var(--fr))] hover:z-9 hover:brightness-105 hover:shadow-[0_50px_90px_rgba(0,0,0,0.65),inset_0_0_0_1px_rgba(255,255,255,0.18),0_0_40px_rgba(255,255,255,0.12)] hover:[transform:translate(var(--fx),calc(var(--fy)-26px))_translateZ(90px)_rotate(var(--fr))_scale(1.06)] motion-reduce:transition-none" style="--i:1;--r0:2deg;--fx:-55px;--fy:18px;--fr:-4deg;--fz:20px">
<div class="absolute inset-0 backdrop-blur-[8px]" aria-hidden="true">
<div class="absolute inset-0 opacity-90 bg-[size:200%_200%] animate-stk28-flow motion-reduce:animate-none bg-linear-[135deg,var(--color-stk28-b1),var(--color-stk28-b2)_45%,var(--color-stk28-b3)]"></div>
<div class="absolute inset-0 opacity-0 mix-blend-soft-light transition-opacity duration-500 group-hover:opacity-100 [background-image:radial-gradient(280px_circle_at_50%_40%,rgba(255,255,255,0.4),transparent_60%)]"></div>
<div class="absolute -top-[60%] -left-[30%] h-[220%] w-[60%] [background-image:linear-gradient(90deg,transparent,rgba(255,255,255,0.5),transparent)] [transform:rotate(18deg)_translateX(-220px)] transition-transform duration-900 ease-stk28 group-hover:[transform:rotate(18deg)_translateX(560px)] motion-reduce:transition-none"></div>
</div>
<div class="absolute inset-0 flex flex-col justify-between p-7 [transform:translateZ(50px)]">
<div class="flex items-center gap-2.5">
<span class="rounded-full border border-white/25 bg-white/16 px-3 py-1.5 text-[0.62rem] font-medium tracking-[0.22em] uppercase text-white backdrop-blur-[6px]">Color</span>
<span class="ml-auto font-stk28-serif text-[1.3rem] italic text-white/75">02</span>
</div>
<div class="flex flex-col gap-4">
<p class="m-0 font-stk28-serif text-5xl leading-[0.92] tracking-[-0.01em] text-white [text-shadow:0_2px_20px_rgba(0,0,0,0.3)]">Warm<br><em class="italic">tones</em></p>
<p class="m-0 max-w-[24ch] text-[0.8rem] leading-relaxed text-white/80">Palette and art direction with a deliberate human warmth.</p>
<span class="inline-flex translate-y-2 items-center gap-1.5 text-[0.78rem] text-white opacity-0 transition-[opacity,transform] duration-500 delay-100 group-hover:translate-y-0 group-hover:opacity-100 motion-reduce:transition-none">View case <span class="grid size-5.5 place-items-center rounded-full bg-white/20 text-[0.8rem]" aria-hidden="true">→</span></span>
</div>
</div>
</div>
<div class="stk-28__card group absolute inset-0 cursor-pointer overflow-hidden rounded-3xl [transform-style:preserve-3d] [z-index:calc(4-var(--i))] brightness-[calc(1-var(--i)*0.13)] shadow-[0_30px_60px_rgba(0,0,0,0.55),inset_0_0_0_1px_rgba(255,255,255,0.08)] [transform:translateZ(calc(var(--i)*-40px))_translateY(calc(var(--i)*10px))_rotate(var(--r0))] transition-[transform,box-shadow,filter] duration-800 ease-stk28 [.stk-28__stage:hover:not(:has(.stk-28__card:hover))_&]:brightness-100 [.stk-28__stage:hover:not(:has(.stk-28__card:hover))_&]:[transform:translate(var(--fx),var(--fy))_translateZ(var(--fz))_rotate(var(--fr))] hover:z-9 hover:brightness-105 hover:shadow-[0_50px_90px_rgba(0,0,0,0.65),inset_0_0_0_1px_rgba(255,255,255,0.18),0_0_40px_rgba(255,255,255,0.12)] hover:[transform:translate(var(--fx),calc(var(--fy)-26px))_translateZ(90px)_rotate(var(--fr))_scale(1.06)] motion-reduce:transition-none" style="--i:2;--r0:-2deg;--fx:55px;--fy:18px;--fr:4deg;--fz:20px">
<div class="absolute inset-0 backdrop-blur-[8px]" aria-hidden="true">
<div class="absolute inset-0 opacity-90 bg-[size:200%_200%] animate-stk28-flow motion-reduce:animate-none bg-linear-[135deg,var(--color-stk28-c1),var(--color-stk28-c2)_50%,var(--color-stk28-c3)]"></div>
<div class="absolute inset-0 opacity-0 mix-blend-soft-light transition-opacity duration-500 group-hover:opacity-100 [background-image:radial-gradient(280px_circle_at_50%_40%,rgba(255,255,255,0.4),transparent_60%)]"></div>
<div class="absolute -top-[60%] -left-[30%] h-[220%] w-[60%] [background-image:linear-gradient(90deg,transparent,rgba(255,255,255,0.5),transparent)] [transform:rotate(18deg)_translateX(-220px)] transition-transform duration-900 ease-stk28 group-hover:[transform:rotate(18deg)_translateX(560px)] motion-reduce:transition-none"></div>
</div>
<div class="absolute inset-0 flex flex-col justify-between p-7 [transform:translateZ(50px)]">
<div class="flex items-center gap-2.5">
<span class="rounded-full border border-white/25 bg-white/16 px-3 py-1.5 text-[0.62rem] font-medium tracking-[0.22em] uppercase text-white backdrop-blur-[6px]">Motion</span>
<span class="ml-auto font-stk28-serif text-[1.3rem] italic text-white/75">03</span>
</div>
<div class="flex flex-col gap-4">
<p class="m-0 font-stk28-serif text-5xl leading-[0.92] tracking-[-0.01em] text-white [text-shadow:0_2px_20px_rgba(0,0,0,0.3)]">In<br><em class="italic">motion</em></p>
<p class="m-0 max-w-[24ch] text-[0.8rem] leading-relaxed text-white/80">A kinetic brand identity that moves with real intent.</p>
<span class="inline-flex translate-y-2 items-center gap-1.5 text-[0.78rem] text-white opacity-0 transition-[opacity,transform] duration-500 delay-100 group-hover:translate-y-0 group-hover:opacity-100 motion-reduce:transition-none">View case <span class="grid size-5.5 place-items-center rounded-full bg-white/20 text-[0.8rem]" aria-hidden="true">→</span></span>
</div>
</div>
</div>
<div class="stk-28__card group absolute inset-0 cursor-pointer overflow-hidden rounded-3xl [transform-style:preserve-3d] [z-index:calc(4-var(--i))] brightness-[calc(1-var(--i)*0.13)] shadow-[0_30px_60px_rgba(0,0,0,0.55),inset_0_0_0_1px_rgba(255,255,255,0.08)] [transform:translateZ(calc(var(--i)*-40px))_translateY(calc(var(--i)*10px))_rotate(var(--r0))] transition-[transform,box-shadow,filter] duration-800 ease-stk28 [.stk-28__stage:hover:not(:has(.stk-28__card:hover))_&]:brightness-100 [.stk-28__stage:hover:not(:has(.stk-28__card:hover))_&]:[transform:translate(var(--fx),var(--fy))_translateZ(var(--fz))_rotate(var(--fr))] hover:z-9 hover:brightness-105 hover:shadow-[0_50px_90px_rgba(0,0,0,0.65),inset_0_0_0_1px_rgba(255,255,255,0.18),0_0_40px_rgba(255,255,255,0.12)] hover:[transform:translate(var(--fx),calc(var(--fy)-26px))_translateZ(90px)_rotate(var(--fr))_scale(1.06)] motion-reduce:transition-none" style="--i:3;--r0:3deg;--fx:165px;--fy:-30px;--fr:12deg;--fz:40px">
<div class="absolute inset-0 backdrop-blur-[8px]" aria-hidden="true">
<div class="absolute inset-0 opacity-90 bg-[size:200%_200%] animate-stk28-flow motion-reduce:animate-none bg-linear-[135deg,var(--color-stk28-d1),var(--color-stk28-d2)_45%,var(--color-stk28-d3)]"></div>
<div class="absolute inset-0 opacity-0 mix-blend-soft-light transition-opacity duration-500 group-hover:opacity-100 [background-image:radial-gradient(280px_circle_at_50%_40%,rgba(255,255,255,0.4),transparent_60%)]"></div>
<div class="absolute -top-[60%] -left-[30%] h-[220%] w-[60%] [background-image:linear-gradient(90deg,transparent,rgba(255,255,255,0.5),transparent)] [transform:rotate(18deg)_translateX(-220px)] transition-transform duration-900 ease-stk28 group-hover:[transform:rotate(18deg)_translateX(560px)] motion-reduce:transition-none"></div>
</div>
<div class="absolute inset-0 flex flex-col justify-between p-7 [transform:translateZ(50px)]">
<div class="flex items-center gap-2.5">
<span class="rounded-full border border-white/25 bg-white/16 px-3 py-1.5 text-[0.62rem] font-medium tracking-[0.22em] uppercase text-white backdrop-blur-[6px]">Type</span>
<span class="ml-auto font-stk28-serif text-[1.3rem] italic text-white/75">04</span>
</div>
<div class="flex flex-col gap-4">
<p class="m-0 font-stk28-serif text-5xl leading-[0.92] tracking-[-0.01em] text-white [text-shadow:0_2px_20px_rgba(0,0,0,0.3)]">Letter<br><em class="italic">forms</em></p>
<p class="m-0 max-w-[24ch] text-[0.8rem] leading-relaxed text-white/80">An editorial typography system built for screens first.</p>
<span class="inline-flex translate-y-2 items-center gap-1.5 text-[0.78rem] text-white opacity-0 transition-[opacity,transform] duration-500 delay-100 group-hover:translate-y-0 group-hover:opacity-100 motion-reduce:transition-none">View case <span class="grid size-5.5 place-items-center rounded-full bg-white/20 text-[0.8rem]" aria-hidden="true">→</span></span>
</div>
</div>
</div>
<div class="absolute -bottom-[70px] left-1/2 h-10 w-60 -translate-x-1/2 blur-[8px] [background-image:radial-gradient(ellipse,rgba(0,0,0,0.6),transparent_70%)]" aria-hidden="true"></div>
</div>
</section>
```
## CSS
```css
@import "tailwindcss";
@theme {
--color-stk28-void: #05060a;
--color-stk28-void: oklch(0.08 0.012 275);
--color-stk28-aur1: rgba(99, 102, 241, 0.35);
--color-stk28-aur2: rgba(236, 72, 153, 0.30);
--color-stk28-aur3: rgba(45, 212, 191, 0.28);
--color-stk28-a1: #1e1b4b;
--color-stk28-a2: #4338ca;
--color-stk28-a3: #6d28d9;
--color-stk28-b1: #831843;
--color-stk28-b2: #be185d;
--color-stk28-b3: #f43f5e;
--color-stk28-c1: #064e3b;
--color-stk28-c2: #0d9488;
--color-stk28-c3: #2dd4bf;
--color-stk28-d1: #7c2d12;
--color-stk28-d2: #ea580c;
--color-stk28-d3: #f59e0b;
--font-stk28-sans: 'Geist', system-ui, sans-serif;
--font-stk28-serif: 'Instrument Serif', Georgia, serif;
--ease-stk28: cubic-bezier(0.16, 1, 0.3, 1);
--animate-stk28-flow: stk-28-flow 8s ease infinite;
--animate-stk28-drift: stk-28-drift 18s ease-in-out infinite alternate;
}
@keyframes stk-28-flow {
0%,
100% {
background-position: 0% 50%;
}
50% {
background-position: 100% 50%;
}
}
@keyframes stk-28-drift {
from {
transform: translate(-3%, -2%) scale(1);
}
to {
transform: translate(4%, 3%) scale(1.12);
}
}
```How this works
The flicker, and why the guard is not optional. If the stage's fan and a card's own lift are both live, hovering a card fires both. They fight on the same 800ms transition, the card slides out from under the cursor, hover is lost, the fan snaps back, hover returns — an infinite flicker. The fix is to scope the fan to ‘stage hovered and nothing inside it hovered’:
[.stk-28__stage:hover:not(:has(.stk-28__card:hover))_&]:
[transform:translate(var(--fx),var(--fy))_translateZ(var(--fz))_rotate(var(--fr))]Tailwind has group-hover: and it has not-*, but not a composition that says ‘my ancestor is hovered but has no hovered descendant’. This is the second and last arbitrary variant in the collection, and like demo 15's it is load-bearing rather than lazy.
Custom properties make four cards one class list. Each card's fanned destination rides the element:
style="--i:0;--r0:-3deg;--fx:-165px;--fy:-30px;--fr:-12deg;--fz:40px"Rest, fan and lift are all expressions over those five numbers, so the class list is copied verbatim four times and only the style attribute differs. That is the practical answer to ‘utilities get repetitive’: move what varies into properties, not into more classes.
Depth reads as light. brightness-[calc(1-var(--i)*0.13)] dims each card by its rank and the fan restores every card to brightness-100, so opening the deck is also the moment the colours arrive.
Make it yours
- Retune the pile with each card's
--r0resting angle; smaller angles read as neat, larger as tossed. - Change the fan by editing the four
--fx/--fy/--fr/--fzsets — the class list never changes. - Rebrand each card through its own
bg-linear-[...]; the animated flow is a shared token. - Change the depth dim with the
0.13factor in the brightness calc. - Slow the gradient drift via
--animate-stk28-flow, or drop it for a static palette.
Gotchas — read before shipping
- Without the
:not(:has(...:hover))guard the fan and the per-card lift fight on the same transition and the deck flickers indefinitely. This is the bug the pattern is famous for. - Give the stage room. Cards fanning to ±165px escape a tight container and clip.
- Each card needs an explicit
z-indexfrom its depth, or source order decides the pile and it will not match the visual stack. - Add
focus-withinparity if you make the cards links — the arbitrary guard only covers hover. mix-blend-soft-lightneeds an opaque layer beneath it; over the page background it does nothing.- Under reduced motion, stop the drift, the sweep and the fan — three separate animations, all decorative.
Browser support
| Chrome | Safari | Firefox | Edge |
|---|---|---|---|
| 111+ | 16.4+ | 128+ | 111+ |
3D transforms, mix-blend-mode and backdrop-filter are broadly supported; :has() needs Chrome 105+, Safari 15.4+, Firefox 121+, and without it the fan simply does not apply while each card still lifts on its own hover.