36 / 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 Spiral Tower Rotating Stacked Cards
Five cards on a rotating helix, pausing on hover or focus. Every card's place on the spiral is one expression over its index — rotateY(calc(var(--i)*72deg))_translateZ(150px)_translateY(calc((var(--i)-2)*60px)) — so changing the card count is changing one number. The honest part of this entry is what it says about itself: a spinning tower cannot carry primary content, and the demo is marked accordingly.
Untitled UI Pro: 5,000+ React + Tailwind CSS v4 components · from $349 one-time
Untitled UI React · Affiliate link
The code
<section class="stk-36 grid w-full min-h-svh place-items-center px-5 py-15 font-stk36-sans bg-stk36-bg [background-image:radial-gradient(circle_at_50%_50%,var(--color-stk36-glow),transparent_70%)]" aria-label="Featured collection">
<div class="group h-100 w-60 [perspective:1200px]" tabindex="0" role="img" aria-label="Five featured cards rotating on a spiral tower. Hover or focus to pause.">
<div class="relative size-full [transform-style:preserve-3d] animate-stk36-spin group-hover:[animation-play-state:paused] group-focus-within:[animation-play-state:paused] motion-reduce:animate-none" aria-hidden="true">
<div class="absolute left-1/2 top-1/2 -ml-15 -mt-20 grid h-40 w-30 place-items-center rounded-2xl font-stk36-display text-[1.4rem] text-white bg-linear-[150deg,var(--color-stk36-a1),var(--color-stk36-a2)] shadow-[0_0_30px_var(--color-stk36-halo)] [transform:rotateY(calc(var(--i)*72deg))_translateZ(150px)_translateY(calc((var(--i)-2)*60px))]" style="--i:0">01</div>
<div class="absolute left-1/2 top-1/2 -ml-15 -mt-20 grid h-40 w-30 place-items-center rounded-2xl font-stk36-display text-[1.4rem] text-white bg-linear-[150deg,var(--color-stk36-b1),var(--color-stk36-b2)] shadow-[0_0_30px_var(--color-stk36-halo)] [transform:rotateY(calc(var(--i)*72deg))_translateZ(150px)_translateY(calc((var(--i)-2)*60px))]" style="--i:1">02</div>
<div class="absolute left-1/2 top-1/2 -ml-15 -mt-20 grid h-40 w-30 place-items-center rounded-2xl font-stk36-display text-[1.4rem] text-white bg-linear-[150deg,var(--color-stk36-c1),var(--color-stk36-c2)] shadow-[0_0_30px_var(--color-stk36-halo)] [transform:rotateY(calc(var(--i)*72deg))_translateZ(150px)_translateY(calc((var(--i)-2)*60px))]" style="--i:2">03</div>
<div class="absolute left-1/2 top-1/2 -ml-15 -mt-20 grid h-40 w-30 place-items-center rounded-2xl font-stk36-display text-[1.4rem] text-white bg-linear-[150deg,var(--color-stk36-d1),var(--color-stk36-d2)] shadow-[0_0_30px_var(--color-stk36-halo)] [transform:rotateY(calc(var(--i)*72deg))_translateZ(150px)_translateY(calc((var(--i)-2)*60px))]" style="--i:3">04</div>
<div class="absolute left-1/2 top-1/2 -ml-15 -mt-20 grid h-40 w-30 place-items-center rounded-2xl font-stk36-display text-[1.4rem] text-stk36-dark bg-linear-[150deg,var(--color-stk36-e1),var(--color-stk36-e2)] shadow-[0_0_30px_var(--color-stk36-halo)] [transform:rotateY(calc(var(--i)*72deg))_translateZ(150px)_translateY(calc((var(--i)-2)*60px))]" style="--i:4">05</div>
</div>
<ol class="sr-only">
<li>01 — Featured collection card one</li>
<li>02 — Featured collection card two</li>
<li>03 — Featured collection card three</li>
<li>04 — Featured collection card four</li>
<li>05 — Featured collection card five</li>
</ol>
</div>
</section><section class="stk-36 grid w-full min-h-svh place-items-center px-5 py-15 font-stk36-sans bg-stk36-bg [background-image:radial-gradient(circle_at_50%_50%,var(--color-stk36-glow),transparent_70%)]" aria-label="Featured collection">
<div class="group h-100 w-60 [perspective:1200px]" tabindex="0" role="img" aria-label="Five featured cards rotating on a spiral tower. Hover or focus to pause.">
<div class="relative size-full [transform-style:preserve-3d] animate-stk36-spin group-hover:[animation-play-state:paused] group-focus-within:[animation-play-state:paused] motion-reduce:animate-none" aria-hidden="true">
<div class="absolute left-1/2 top-1/2 -ml-15 -mt-20 grid h-40 w-30 place-items-center rounded-2xl font-stk36-display text-[1.4rem] text-white bg-linear-[150deg,var(--color-stk36-a1),var(--color-stk36-a2)] shadow-[0_0_30px_var(--color-stk36-halo)] [transform:rotateY(calc(var(--i)*72deg))_translateZ(150px)_translateY(calc((var(--i)-2)*60px))]" style="--i:0">01</div>
<div class="absolute left-1/2 top-1/2 -ml-15 -mt-20 grid h-40 w-30 place-items-center rounded-2xl font-stk36-display text-[1.4rem] text-white bg-linear-[150deg,var(--color-stk36-b1),var(--color-stk36-b2)] shadow-[0_0_30px_var(--color-stk36-halo)] [transform:rotateY(calc(var(--i)*72deg))_translateZ(150px)_translateY(calc((var(--i)-2)*60px))]" style="--i:1">02</div>
<div class="absolute left-1/2 top-1/2 -ml-15 -mt-20 grid h-40 w-30 place-items-center rounded-2xl font-stk36-display text-[1.4rem] text-white bg-linear-[150deg,var(--color-stk36-c1),var(--color-stk36-c2)] shadow-[0_0_30px_var(--color-stk36-halo)] [transform:rotateY(calc(var(--i)*72deg))_translateZ(150px)_translateY(calc((var(--i)-2)*60px))]" style="--i:2">03</div>
<div class="absolute left-1/2 top-1/2 -ml-15 -mt-20 grid h-40 w-30 place-items-center rounded-2xl font-stk36-display text-[1.4rem] text-white bg-linear-[150deg,var(--color-stk36-d1),var(--color-stk36-d2)] shadow-[0_0_30px_var(--color-stk36-halo)] [transform:rotateY(calc(var(--i)*72deg))_translateZ(150px)_translateY(calc((var(--i)-2)*60px))]" style="--i:3">04</div>
<div class="absolute left-1/2 top-1/2 -ml-15 -mt-20 grid h-40 w-30 place-items-center rounded-2xl font-stk36-display text-[1.4rem] text-stk36-dark bg-linear-[150deg,var(--color-stk36-e1),var(--color-stk36-e2)] shadow-[0_0_30px_var(--color-stk36-halo)] [transform:rotateY(calc(var(--i)*72deg))_translateZ(150px)_translateY(calc((var(--i)-2)*60px))]" style="--i:4">05</div>
</div>
<ol class="sr-only">
<li>01 — Featured collection card one</li>
<li>02 — Featured collection card two</li>
<li>03 — Featured collection card three</li>
<li>04 — Featured collection card four</li>
<li>05 — Featured collection card five</li>
</ol>
</div>
</section>@import "tailwindcss";
@theme {
--color-stk36-bg: #080410;
--color-stk36-bg: oklch(0.09 0.020 300);
--color-stk36-glow: #1e0a2e;
--color-stk36-glow: oklch(0.20 0.055 300);
--color-stk36-dark: #4a1a2a;
--color-stk36-dark: oklch(0.32 0.070 10);
--color-stk36-halo: rgba(180, 100, 255, 0.25);
--color-stk36-a1: #ee0979;
--color-stk36-a2: #ff6a00;
--color-stk36-b1: #7f00ff;
--color-stk36-b2: #e100ff;
--color-stk36-c1: #00c6ff;
--color-stk36-c2: #0072ff;
--color-stk36-d1: #11998e;
--color-stk36-d2: #38ef7d;
--color-stk36-e1: #f7971e;
--color-stk36-e2: #ffd200;
--font-stk36-sans: 'Jost', system-ui, sans-serif;
--font-stk36-display: 'Major Mono Display', ui-monospace, monospace;
--animate-stk36-spin: stk-36-spin 14s linear infinite;
}
@keyframes stk-36-spin {
to {
transform: rotateY(360deg);
}
}@import "tailwindcss";
@theme {
--color-stk36-bg: #080410;
--color-stk36-bg: oklch(0.09 0.020 300);
--color-stk36-glow: #1e0a2e;
--color-stk36-glow: oklch(0.20 0.055 300);
--color-stk36-dark: #4a1a2a;
--color-stk36-dark: oklch(0.32 0.070 10);
--color-stk36-halo: rgba(180, 100, 255, 0.25);
--color-stk36-a1: #ee0979;
--color-stk36-a2: #ff6a00;
--color-stk36-b1: #7f00ff;
--color-stk36-b2: #e100ff;
--color-stk36-c1: #00c6ff;
--color-stk36-c2: #0072ff;
--color-stk36-d1: #11998e;
--color-stk36-d2: #38ef7d;
--color-stk36-e1: #f7971e;
--color-stk36-e2: #ffd200;
--font-stk36-sans: 'Jost', system-ui, sans-serif;
--font-stk36-display: 'Major Mono Display', ui-monospace, monospace;
--animate-stk36-spin: stk-36-spin 14s linear infinite;
}
@keyframes stk-36-spin {
to {
transform: rotateY(360deg);
}
}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-stk36-bg: #080410;
--color-stk36-bg: oklch(0.09 0.020 300);
--color-stk36-glow: #1e0a2e;
--color-stk36-glow: oklch(0.20 0.055 300);
--color-stk36-dark: #4a1a2a;
--color-stk36-dark: oklch(0.32 0.070 10);
--color-stk36-halo: rgba(180, 100, 255, 0.25);
--color-stk36-a1: #ee0979;
--color-stk36-a2: #ff6a00;
--color-stk36-b1: #7f00ff;
--color-stk36-b2: #e100ff;
--color-stk36-c1: #00c6ff;
--color-stk36-c2: #0072ff;
--color-stk36-d1: #11998e;
--color-stk36-d2: #38ef7d;
--color-stk36-e1: #f7971e;
--color-stk36-e2: #ffd200;
--font-stk36-sans: 'Jost', system-ui, sans-serif;
--font-stk36-display: 'Major Mono Display', ui-monospace, monospace;
--animate-stk36-spin: stk-36-spin 14s linear infinite;
}
@keyframes stk-36-spin {
to {
transform: rotateY(360deg);
}
}@import "tailwindcss";
@theme {
--color-stk36-bg: #080410;
--color-stk36-bg: oklch(0.09 0.020 300);
--color-stk36-glow: #1e0a2e;
--color-stk36-glow: oklch(0.20 0.055 300);
--color-stk36-dark: #4a1a2a;
--color-stk36-dark: oklch(0.32 0.070 10);
--color-stk36-halo: rgba(180, 100, 255, 0.25);
--color-stk36-a1: #ee0979;
--color-stk36-a2: #ff6a00;
--color-stk36-b1: #7f00ff;
--color-stk36-b2: #e100ff;
--color-stk36-c1: #00c6ff;
--color-stk36-c2: #0072ff;
--color-stk36-d1: #11998e;
--color-stk36-d2: #38ef7d;
--color-stk36-e1: #f7971e;
--color-stk36-e2: #ffd200;
--font-stk36-sans: 'Jost', system-ui, sans-serif;
--font-stk36-display: 'Major Mono Display', ui-monospace, monospace;
--animate-stk36-spin: stk-36-spin 14s linear infinite;
}
@keyframes stk-36-spin {
to {
transform: rotateY(360deg);
}
}<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=Major+Mono+Display&family=Jost: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=Major+Mono+Display&family=Jost: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. - Five
:nth-childrules become one index expression: an even angular step, a fixed radius and a linear rise, all over--i. - Changing the card count is changing two numbers — the
360/nstep and the centring constant in the rise — not rewriting a rule per card. - The loop is an
--animate-*token and the pause is[animation-play-state:paused]under bothgroup-hover:andgroup-focus-within:, because unstoppable motion is an accessibility failure. - The tower is
aria-hiddenwith ansr-onlyordered list beside it: text on a rotating card cannot be read, so the content has to exist somewhere it can. - Reduced motion removes the animation rather than slowing it, leaving the cards in their composed static pose.
- This is the last of the collection's 3D demos and the fifth repetition of one rule:
perspectiveon the parent,preserve-3don the child that rotates.
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 Spiral Tower Rotating Stacked Cards
Source: https://codefronts.com/components/tailwind-stacked-cards/tailwind-spiral-tower-rotating-stacked-cards/
Five cards on a rotating helix, pausing on hover or focus. Every card's place on the spiral is one expression over its index — rotateY(calc(var(--i)*72deg))_translateZ(150px)_translateY(calc((var(--i)-2)*60px)) — so changing the card count is changing one number. The honest part of this entry is what it says about itself: a spinning tower cannot carry primary content, and the demo is marked accordingly.
## HTML
```html
<section class="stk-36 grid w-full min-h-svh place-items-center px-5 py-15 font-stk36-sans bg-stk36-bg [background-image:radial-gradient(circle_at_50%_50%,var(--color-stk36-glow),transparent_70%)]" aria-label="Featured collection">
<div class="group h-100 w-60 [perspective:1200px]" tabindex="0" role="img" aria-label="Five featured cards rotating on a spiral tower. Hover or focus to pause.">
<div class="relative size-full [transform-style:preserve-3d] animate-stk36-spin group-hover:[animation-play-state:paused] group-focus-within:[animation-play-state:paused] motion-reduce:animate-none" aria-hidden="true">
<div class="absolute left-1/2 top-1/2 -ml-15 -mt-20 grid h-40 w-30 place-items-center rounded-2xl font-stk36-display text-[1.4rem] text-white bg-linear-[150deg,var(--color-stk36-a1),var(--color-stk36-a2)] shadow-[0_0_30px_var(--color-stk36-halo)] [transform:rotateY(calc(var(--i)*72deg))_translateZ(150px)_translateY(calc((var(--i)-2)*60px))]" style="--i:0">01</div>
<div class="absolute left-1/2 top-1/2 -ml-15 -mt-20 grid h-40 w-30 place-items-center rounded-2xl font-stk36-display text-[1.4rem] text-white bg-linear-[150deg,var(--color-stk36-b1),var(--color-stk36-b2)] shadow-[0_0_30px_var(--color-stk36-halo)] [transform:rotateY(calc(var(--i)*72deg))_translateZ(150px)_translateY(calc((var(--i)-2)*60px))]" style="--i:1">02</div>
<div class="absolute left-1/2 top-1/2 -ml-15 -mt-20 grid h-40 w-30 place-items-center rounded-2xl font-stk36-display text-[1.4rem] text-white bg-linear-[150deg,var(--color-stk36-c1),var(--color-stk36-c2)] shadow-[0_0_30px_var(--color-stk36-halo)] [transform:rotateY(calc(var(--i)*72deg))_translateZ(150px)_translateY(calc((var(--i)-2)*60px))]" style="--i:2">03</div>
<div class="absolute left-1/2 top-1/2 -ml-15 -mt-20 grid h-40 w-30 place-items-center rounded-2xl font-stk36-display text-[1.4rem] text-white bg-linear-[150deg,var(--color-stk36-d1),var(--color-stk36-d2)] shadow-[0_0_30px_var(--color-stk36-halo)] [transform:rotateY(calc(var(--i)*72deg))_translateZ(150px)_translateY(calc((var(--i)-2)*60px))]" style="--i:3">04</div>
<div class="absolute left-1/2 top-1/2 -ml-15 -mt-20 grid h-40 w-30 place-items-center rounded-2xl font-stk36-display text-[1.4rem] text-stk36-dark bg-linear-[150deg,var(--color-stk36-e1),var(--color-stk36-e2)] shadow-[0_0_30px_var(--color-stk36-halo)] [transform:rotateY(calc(var(--i)*72deg))_translateZ(150px)_translateY(calc((var(--i)-2)*60px))]" style="--i:4">05</div>
</div>
<ol class="sr-only">
<li>01 — Featured collection card one</li>
<li>02 — Featured collection card two</li>
<li>03 — Featured collection card three</li>
<li>04 — Featured collection card four</li>
<li>05 — Featured collection card five</li>
</ol>
</div>
</section>
```
## CSS
```css
@import "tailwindcss";
@theme {
--color-stk36-bg: #080410;
--color-stk36-bg: oklch(0.09 0.020 300);
--color-stk36-glow: #1e0a2e;
--color-stk36-glow: oklch(0.20 0.055 300);
--color-stk36-dark: #4a1a2a;
--color-stk36-dark: oklch(0.32 0.070 10);
--color-stk36-halo: rgba(180, 100, 255, 0.25);
--color-stk36-a1: #ee0979;
--color-stk36-a2: #ff6a00;
--color-stk36-b1: #7f00ff;
--color-stk36-b2: #e100ff;
--color-stk36-c1: #00c6ff;
--color-stk36-c2: #0072ff;
--color-stk36-d1: #11998e;
--color-stk36-d2: #38ef7d;
--color-stk36-e1: #f7971e;
--color-stk36-e2: #ffd200;
--font-stk36-sans: 'Jost', system-ui, sans-serif;
--font-stk36-display: 'Major Mono Display', ui-monospace, monospace;
--animate-stk36-spin: stk-36-spin 14s linear infinite;
}
@keyframes stk-36-spin {
to {
transform: rotateY(360deg);
}
}
```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 Spiral Tower Rotating Stacked Cards
Source: https://codefronts.com/components/tailwind-stacked-cards/tailwind-spiral-tower-rotating-stacked-cards/
Five cards on a rotating helix, pausing on hover or focus. Every card's place on the spiral is one expression over its index — rotateY(calc(var(--i)*72deg))_translateZ(150px)_translateY(calc((var(--i)-2)*60px)) — so changing the card count is changing one number. The honest part of this entry is what it says about itself: a spinning tower cannot carry primary content, and the demo is marked accordingly.
## HTML
```html
<section class="stk-36 grid w-full min-h-svh place-items-center px-5 py-15 font-stk36-sans bg-stk36-bg [background-image:radial-gradient(circle_at_50%_50%,var(--color-stk36-glow),transparent_70%)]" aria-label="Featured collection">
<div class="group h-100 w-60 [perspective:1200px]" tabindex="0" role="img" aria-label="Five featured cards rotating on a spiral tower. Hover or focus to pause.">
<div class="relative size-full [transform-style:preserve-3d] animate-stk36-spin group-hover:[animation-play-state:paused] group-focus-within:[animation-play-state:paused] motion-reduce:animate-none" aria-hidden="true">
<div class="absolute left-1/2 top-1/2 -ml-15 -mt-20 grid h-40 w-30 place-items-center rounded-2xl font-stk36-display text-[1.4rem] text-white bg-linear-[150deg,var(--color-stk36-a1),var(--color-stk36-a2)] shadow-[0_0_30px_var(--color-stk36-halo)] [transform:rotateY(calc(var(--i)*72deg))_translateZ(150px)_translateY(calc((var(--i)-2)*60px))]" style="--i:0">01</div>
<div class="absolute left-1/2 top-1/2 -ml-15 -mt-20 grid h-40 w-30 place-items-center rounded-2xl font-stk36-display text-[1.4rem] text-white bg-linear-[150deg,var(--color-stk36-b1),var(--color-stk36-b2)] shadow-[0_0_30px_var(--color-stk36-halo)] [transform:rotateY(calc(var(--i)*72deg))_translateZ(150px)_translateY(calc((var(--i)-2)*60px))]" style="--i:1">02</div>
<div class="absolute left-1/2 top-1/2 -ml-15 -mt-20 grid h-40 w-30 place-items-center rounded-2xl font-stk36-display text-[1.4rem] text-white bg-linear-[150deg,var(--color-stk36-c1),var(--color-stk36-c2)] shadow-[0_0_30px_var(--color-stk36-halo)] [transform:rotateY(calc(var(--i)*72deg))_translateZ(150px)_translateY(calc((var(--i)-2)*60px))]" style="--i:2">03</div>
<div class="absolute left-1/2 top-1/2 -ml-15 -mt-20 grid h-40 w-30 place-items-center rounded-2xl font-stk36-display text-[1.4rem] text-white bg-linear-[150deg,var(--color-stk36-d1),var(--color-stk36-d2)] shadow-[0_0_30px_var(--color-stk36-halo)] [transform:rotateY(calc(var(--i)*72deg))_translateZ(150px)_translateY(calc((var(--i)-2)*60px))]" style="--i:3">04</div>
<div class="absolute left-1/2 top-1/2 -ml-15 -mt-20 grid h-40 w-30 place-items-center rounded-2xl font-stk36-display text-[1.4rem] text-stk36-dark bg-linear-[150deg,var(--color-stk36-e1),var(--color-stk36-e2)] shadow-[0_0_30px_var(--color-stk36-halo)] [transform:rotateY(calc(var(--i)*72deg))_translateZ(150px)_translateY(calc((var(--i)-2)*60px))]" style="--i:4">05</div>
</div>
<ol class="sr-only">
<li>01 — Featured collection card one</li>
<li>02 — Featured collection card two</li>
<li>03 — Featured collection card three</li>
<li>04 — Featured collection card four</li>
<li>05 — Featured collection card five</li>
</ol>
</div>
</section>
```
## CSS
```css
@import "tailwindcss";
@theme {
--color-stk36-bg: #080410;
--color-stk36-bg: oklch(0.09 0.020 300);
--color-stk36-glow: #1e0a2e;
--color-stk36-glow: oklch(0.20 0.055 300);
--color-stk36-dark: #4a1a2a;
--color-stk36-dark: oklch(0.32 0.070 10);
--color-stk36-halo: rgba(180, 100, 255, 0.25);
--color-stk36-a1: #ee0979;
--color-stk36-a2: #ff6a00;
--color-stk36-b1: #7f00ff;
--color-stk36-b2: #e100ff;
--color-stk36-c1: #00c6ff;
--color-stk36-c2: #0072ff;
--color-stk36-d1: #11998e;
--color-stk36-d2: #38ef7d;
--color-stk36-e1: #f7971e;
--color-stk36-e2: #ffd200;
--font-stk36-sans: 'Jost', system-ui, sans-serif;
--font-stk36-display: 'Major Mono Display', ui-monospace, monospace;
--animate-stk36-spin: stk-36-spin 14s linear infinite;
}
@keyframes stk-36-spin {
to {
transform: rotateY(360deg);
}
}
```How this works
One expression, any card count. The angular step is 360/n, the radius is constant, and the rise is linear about the middle:
[transform:rotateY(calc(var(--i)*72deg))_translateZ(150px)
_translateY(calc((var(--i)-2)*60px))]Five cards means 72deg and a centring constant of 2; six means 60deg and 2.5. Five :nth-child rules reduce to that, and nothing else in the demo knows how many cards there are.
Pause, and pause for the keyboard too. [animation-play-state:paused] under both group-hover: and group-focus-within: means the tower stops for a pointer and for a tab. Continuous motion that cannot be stopped is a WCAG failure, not a stylistic risk.
Be honest about what this is for. Text on a rotating card cannot be read, so the tower is decorative by construction: it carries numerals, is marked aria-hidden, and the real list sits beside it in an sr-only block. Under reduced motion the animation is removed entirely and the cards hold a static composed pose — which is the accommodation, not a slower spin.
Cost. Five composited 3D layers rotating continuously is smooth on desktop and marginal on older phones. If it must ship on mobile, reduce the card count before reducing the frame rate.
Make it yours
- Change the card count by editing the angular step (
360/n) and the centring constant in the rise. - Set the tower radius with the
translateZ(150px). - Tune the spin via
--animate-stk36-spin— 14s is the default, 30s reads as calm. - Rebrand each card through its own gradient pair; the helix is palette-agnostic.
- Replace the numerals with real content only if you also stop the rotation by default.
Gotchas — read before shipping
- A continuous rotation must be pausable and must respect
prefers-reduced-motion. Both are requirements, not polish. - Do not put primary content on a spinning card — it cannot be read while it moves. Mirror it as text.
- Provide the
group-focus-within:pause as well as the hover one, or keyboard users cannot stop it at all. - Five composited layers rotating is smooth on desktop and marginal on older phones; cut cards rather than frames.
perspectiveon the stage,preserve-3don the spiral — the last repetition of the rule that governs every 3D demo here.- Under reduced motion, hold a composed static pose rather than freezing at whatever frame the animation happened to reach.
Browser support
| Chrome | Safari | Firefox | Edge |
|---|---|---|---|
| 111+ | 16.4+ | 128+ | 111+ |
3D transforms, keyframes and animation-play-state are universally supported (Chrome 36+, Safari 9+, Firefox 16+). The floor is Tailwind v4's own generated sheet.