11 / 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 Fan Spread Hand of Cards
A poker hand, fanned from one anchor. Each card carries its own --a and every state repeats that angle: rest is [transform:rotate(var(--a))], raised is hover:[transform:rotate(var(--a))_translateY(-40px)_scale(1.05)]. Because the pivot is shared and low, spacing stays even however many cards you deal — adding a sixth is one angle, not a new rule.
Untitled UI Pro: 5,000+ React + Tailwind CSS v4 components · from $349 one-time
Untitled UI React · Affiliate link
The code
<section class="stk-11 w-full min-h-svh flex items-end justify-center px-5 pt-15 pb-[22vh] font-stk11-sans bg-stk11-felt [background-image:radial-gradient(circle_at_50%_120%,var(--color-stk11-glow),transparent_70%)]" aria-label="Fanned hand of cards">
<div class="relative w-50 h-50">
<button class="absolute left-1/2 bottom-0 -ml-15 flex h-42.5 w-30 flex-col items-start justify-between border-0 cursor-pointer rounded-[14px] bg-white px-4 py-3.5 text-3xl font-extrabold text-stk11-s1 shadow-[0_14px_30px_-14px_rgba(0,0,0,0.7)] [transform-origin:50%_130%] [transform:rotate(var(--a))] transition-[transform,box-shadow] duration-350 ease-stk11 hover:z-10 hover:shadow-[0_26px_44px_-18px_rgba(0,0,0,0.8)] hover:[transform:rotate(var(--a))_translateY(-40px)_scale(1.05)] focus-visible:z-10 focus-visible:outline-none focus-visible:shadow-[0_0_0_3px_var(--color-stk11-ring),0_26px_44px_-18px_rgba(0,0,0,0.8)] focus-visible:[transform:rotate(var(--a))_translateY(-40px)_scale(1.05)] motion-reduce:hover:[transform:rotate(var(--a))_translateY(-40px)] motion-reduce:focus-visible:[transform:rotate(var(--a))_translateY(-40px)]" type="button" style="--a:-10deg">A<small class="self-end text-[28px]">♠</small></button>
<button class="absolute left-1/2 bottom-0 -ml-15 flex h-42.5 w-30 flex-col items-start justify-between border-0 cursor-pointer rounded-[14px] bg-white px-4 py-3.5 text-3xl font-extrabold text-stk11-s2 shadow-[0_14px_30px_-14px_rgba(0,0,0,0.7)] [transform-origin:50%_130%] [transform:rotate(var(--a))] transition-[transform,box-shadow] duration-350 ease-stk11 hover:z-10 hover:shadow-[0_26px_44px_-18px_rgba(0,0,0,0.8)] hover:[transform:rotate(var(--a))_translateY(-40px)_scale(1.05)] focus-visible:z-10 focus-visible:outline-none focus-visible:shadow-[0_0_0_3px_var(--color-stk11-ring),0_26px_44px_-18px_rgba(0,0,0,0.8)] focus-visible:[transform:rotate(var(--a))_translateY(-40px)_scale(1.05)] motion-reduce:hover:[transform:rotate(var(--a))_translateY(-40px)] motion-reduce:focus-visible:[transform:rotate(var(--a))_translateY(-40px)]" type="button" style="--a:-5deg">K<small class="self-end text-[28px]">♥</small></button>
<button class="absolute left-1/2 bottom-0 -ml-15 flex h-42.5 w-30 flex-col items-start justify-between border-0 cursor-pointer rounded-[14px] bg-white px-4 py-3.5 text-3xl font-extrabold text-stk11-s3 shadow-[0_14px_30px_-14px_rgba(0,0,0,0.7)] [transform-origin:50%_130%] [transform:rotate(var(--a))] transition-[transform,box-shadow] duration-350 ease-stk11 hover:z-10 hover:shadow-[0_26px_44px_-18px_rgba(0,0,0,0.8)] hover:[transform:rotate(var(--a))_translateY(-40px)_scale(1.05)] focus-visible:z-10 focus-visible:outline-none focus-visible:shadow-[0_0_0_3px_var(--color-stk11-ring),0_26px_44px_-18px_rgba(0,0,0,0.8)] focus-visible:[transform:rotate(var(--a))_translateY(-40px)_scale(1.05)] motion-reduce:hover:[transform:rotate(var(--a))_translateY(-40px)] motion-reduce:focus-visible:[transform:rotate(var(--a))_translateY(-40px)]" type="button" style="--a:0deg">Q<small class="self-end text-[28px]">♦</small></button>
<button class="absolute left-1/2 bottom-0 -ml-15 flex h-42.5 w-30 flex-col items-start justify-between border-0 cursor-pointer rounded-[14px] bg-white px-4 py-3.5 text-3xl font-extrabold text-stk11-s4 shadow-[0_14px_30px_-14px_rgba(0,0,0,0.7)] [transform-origin:50%_130%] [transform:rotate(var(--a))] transition-[transform,box-shadow] duration-350 ease-stk11 hover:z-10 hover:shadow-[0_26px_44px_-18px_rgba(0,0,0,0.8)] hover:[transform:rotate(var(--a))_translateY(-40px)_scale(1.05)] focus-visible:z-10 focus-visible:outline-none focus-visible:shadow-[0_0_0_3px_var(--color-stk11-ring),0_26px_44px_-18px_rgba(0,0,0,0.8)] focus-visible:[transform:rotate(var(--a))_translateY(-40px)_scale(1.05)] motion-reduce:hover:[transform:rotate(var(--a))_translateY(-40px)] motion-reduce:focus-visible:[transform:rotate(var(--a))_translateY(-40px)]" type="button" style="--a:5deg">J<small class="self-end text-[28px]">♣</small></button>
<button class="absolute left-1/2 bottom-0 -ml-15 flex h-42.5 w-30 flex-col items-start justify-between border-0 cursor-pointer rounded-[14px] bg-white px-4 py-3.5 text-3xl font-extrabold text-stk11-s5 shadow-[0_14px_30px_-14px_rgba(0,0,0,0.7)] [transform-origin:50%_130%] [transform:rotate(var(--a))] transition-[transform,box-shadow] duration-350 ease-stk11 hover:z-10 hover:shadow-[0_26px_44px_-18px_rgba(0,0,0,0.8)] hover:[transform:rotate(var(--a))_translateY(-40px)_scale(1.05)] focus-visible:z-10 focus-visible:outline-none focus-visible:shadow-[0_0_0_3px_var(--color-stk11-ring),0_26px_44px_-18px_rgba(0,0,0,0.8)] focus-visible:[transform:rotate(var(--a))_translateY(-40px)_scale(1.05)] motion-reduce:hover:[transform:rotate(var(--a))_translateY(-40px)] motion-reduce:focus-visible:[transform:rotate(var(--a))_translateY(-40px)]" type="button" style="--a:10deg">10<small class="self-end text-[28px]">★</small></button>
</div>
</section><section class="stk-11 w-full min-h-svh flex items-end justify-center px-5 pt-15 pb-[22vh] font-stk11-sans bg-stk11-felt [background-image:radial-gradient(circle_at_50%_120%,var(--color-stk11-glow),transparent_70%)]" aria-label="Fanned hand of cards">
<div class="relative w-50 h-50">
<button class="absolute left-1/2 bottom-0 -ml-15 flex h-42.5 w-30 flex-col items-start justify-between border-0 cursor-pointer rounded-[14px] bg-white px-4 py-3.5 text-3xl font-extrabold text-stk11-s1 shadow-[0_14px_30px_-14px_rgba(0,0,0,0.7)] [transform-origin:50%_130%] [transform:rotate(var(--a))] transition-[transform,box-shadow] duration-350 ease-stk11 hover:z-10 hover:shadow-[0_26px_44px_-18px_rgba(0,0,0,0.8)] hover:[transform:rotate(var(--a))_translateY(-40px)_scale(1.05)] focus-visible:z-10 focus-visible:outline-none focus-visible:shadow-[0_0_0_3px_var(--color-stk11-ring),0_26px_44px_-18px_rgba(0,0,0,0.8)] focus-visible:[transform:rotate(var(--a))_translateY(-40px)_scale(1.05)] motion-reduce:hover:[transform:rotate(var(--a))_translateY(-40px)] motion-reduce:focus-visible:[transform:rotate(var(--a))_translateY(-40px)]" type="button" style="--a:-10deg">A<small class="self-end text-[28px]">♠</small></button>
<button class="absolute left-1/2 bottom-0 -ml-15 flex h-42.5 w-30 flex-col items-start justify-between border-0 cursor-pointer rounded-[14px] bg-white px-4 py-3.5 text-3xl font-extrabold text-stk11-s2 shadow-[0_14px_30px_-14px_rgba(0,0,0,0.7)] [transform-origin:50%_130%] [transform:rotate(var(--a))] transition-[transform,box-shadow] duration-350 ease-stk11 hover:z-10 hover:shadow-[0_26px_44px_-18px_rgba(0,0,0,0.8)] hover:[transform:rotate(var(--a))_translateY(-40px)_scale(1.05)] focus-visible:z-10 focus-visible:outline-none focus-visible:shadow-[0_0_0_3px_var(--color-stk11-ring),0_26px_44px_-18px_rgba(0,0,0,0.8)] focus-visible:[transform:rotate(var(--a))_translateY(-40px)_scale(1.05)] motion-reduce:hover:[transform:rotate(var(--a))_translateY(-40px)] motion-reduce:focus-visible:[transform:rotate(var(--a))_translateY(-40px)]" type="button" style="--a:-5deg">K<small class="self-end text-[28px]">♥</small></button>
<button class="absolute left-1/2 bottom-0 -ml-15 flex h-42.5 w-30 flex-col items-start justify-between border-0 cursor-pointer rounded-[14px] bg-white px-4 py-3.5 text-3xl font-extrabold text-stk11-s3 shadow-[0_14px_30px_-14px_rgba(0,0,0,0.7)] [transform-origin:50%_130%] [transform:rotate(var(--a))] transition-[transform,box-shadow] duration-350 ease-stk11 hover:z-10 hover:shadow-[0_26px_44px_-18px_rgba(0,0,0,0.8)] hover:[transform:rotate(var(--a))_translateY(-40px)_scale(1.05)] focus-visible:z-10 focus-visible:outline-none focus-visible:shadow-[0_0_0_3px_var(--color-stk11-ring),0_26px_44px_-18px_rgba(0,0,0,0.8)] focus-visible:[transform:rotate(var(--a))_translateY(-40px)_scale(1.05)] motion-reduce:hover:[transform:rotate(var(--a))_translateY(-40px)] motion-reduce:focus-visible:[transform:rotate(var(--a))_translateY(-40px)]" type="button" style="--a:0deg">Q<small class="self-end text-[28px]">♦</small></button>
<button class="absolute left-1/2 bottom-0 -ml-15 flex h-42.5 w-30 flex-col items-start justify-between border-0 cursor-pointer rounded-[14px] bg-white px-4 py-3.5 text-3xl font-extrabold text-stk11-s4 shadow-[0_14px_30px_-14px_rgba(0,0,0,0.7)] [transform-origin:50%_130%] [transform:rotate(var(--a))] transition-[transform,box-shadow] duration-350 ease-stk11 hover:z-10 hover:shadow-[0_26px_44px_-18px_rgba(0,0,0,0.8)] hover:[transform:rotate(var(--a))_translateY(-40px)_scale(1.05)] focus-visible:z-10 focus-visible:outline-none focus-visible:shadow-[0_0_0_3px_var(--color-stk11-ring),0_26px_44px_-18px_rgba(0,0,0,0.8)] focus-visible:[transform:rotate(var(--a))_translateY(-40px)_scale(1.05)] motion-reduce:hover:[transform:rotate(var(--a))_translateY(-40px)] motion-reduce:focus-visible:[transform:rotate(var(--a))_translateY(-40px)]" type="button" style="--a:5deg">J<small class="self-end text-[28px]">♣</small></button>
<button class="absolute left-1/2 bottom-0 -ml-15 flex h-42.5 w-30 flex-col items-start justify-between border-0 cursor-pointer rounded-[14px] bg-white px-4 py-3.5 text-3xl font-extrabold text-stk11-s5 shadow-[0_14px_30px_-14px_rgba(0,0,0,0.7)] [transform-origin:50%_130%] [transform:rotate(var(--a))] transition-[transform,box-shadow] duration-350 ease-stk11 hover:z-10 hover:shadow-[0_26px_44px_-18px_rgba(0,0,0,0.8)] hover:[transform:rotate(var(--a))_translateY(-40px)_scale(1.05)] focus-visible:z-10 focus-visible:outline-none focus-visible:shadow-[0_0_0_3px_var(--color-stk11-ring),0_26px_44px_-18px_rgba(0,0,0,0.8)] focus-visible:[transform:rotate(var(--a))_translateY(-40px)_scale(1.05)] motion-reduce:hover:[transform:rotate(var(--a))_translateY(-40px)] motion-reduce:focus-visible:[transform:rotate(var(--a))_translateY(-40px)]" type="button" style="--a:10deg">10<small class="self-end text-[28px]">★</small></button>
</div>
</section>@import "tailwindcss";
@theme {
--color-stk11-felt: #0d281c;
--color-stk11-felt: oklch(0.26 0.055 155);
--color-stk11-glow: #1f5136;
--color-stk11-glow: oklch(0.38 0.075 155);
--color-stk11-ring: #e6c65a;
--color-stk11-ring: oklch(0.83 0.14 90);
--color-stk11-s1: #cf5f4b;
--color-stk11-s1: oklch(0.62 0.16 20);
--color-stk11-s2: #c98a30;
--color-stk11-s2: oklch(0.64 0.15 60);
--color-stk11-s3: #45a878;
--color-stk11-s3: oklch(0.64 0.15 150);
--color-stk11-s4: #4a86cf;
--color-stk11-s4: oklch(0.60 0.16 235);
--color-stk11-s5: #9468cc;
--color-stk11-s5: oklch(0.60 0.16 300);
--font-stk11-sans: 'Archivo', system-ui, sans-serif;
--ease-stk11: cubic-bezier(0.3, 0.9, 0.3, 1);
}@import "tailwindcss";
@theme {
--color-stk11-felt: #0d281c;
--color-stk11-felt: oklch(0.26 0.055 155);
--color-stk11-glow: #1f5136;
--color-stk11-glow: oklch(0.38 0.075 155);
--color-stk11-ring: #e6c65a;
--color-stk11-ring: oklch(0.83 0.14 90);
--color-stk11-s1: #cf5f4b;
--color-stk11-s1: oklch(0.62 0.16 20);
--color-stk11-s2: #c98a30;
--color-stk11-s2: oklch(0.64 0.15 60);
--color-stk11-s3: #45a878;
--color-stk11-s3: oklch(0.64 0.15 150);
--color-stk11-s4: #4a86cf;
--color-stk11-s4: oklch(0.60 0.16 235);
--color-stk11-s5: #9468cc;
--color-stk11-s5: oklch(0.60 0.16 300);
--font-stk11-sans: 'Archivo', system-ui, sans-serif;
--ease-stk11: 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-stk11-felt: #0d281c;
--color-stk11-felt: oklch(0.26 0.055 155);
--color-stk11-glow: #1f5136;
--color-stk11-glow: oklch(0.38 0.075 155);
--color-stk11-ring: #e6c65a;
--color-stk11-ring: oklch(0.83 0.14 90);
--color-stk11-s1: #cf5f4b;
--color-stk11-s1: oklch(0.62 0.16 20);
--color-stk11-s2: #c98a30;
--color-stk11-s2: oklch(0.64 0.15 60);
--color-stk11-s3: #45a878;
--color-stk11-s3: oklch(0.64 0.15 150);
--color-stk11-s4: #4a86cf;
--color-stk11-s4: oklch(0.60 0.16 235);
--color-stk11-s5: #9468cc;
--color-stk11-s5: oklch(0.60 0.16 300);
--font-stk11-sans: 'Archivo', system-ui, sans-serif;
--ease-stk11: cubic-bezier(0.3, 0.9, 0.3, 1);
}@import "tailwindcss";
@theme {
--color-stk11-felt: #0d281c;
--color-stk11-felt: oklch(0.26 0.055 155);
--color-stk11-glow: #1f5136;
--color-stk11-glow: oklch(0.38 0.075 155);
--color-stk11-ring: #e6c65a;
--color-stk11-ring: oklch(0.83 0.14 90);
--color-stk11-s1: #cf5f4b;
--color-stk11-s1: oklch(0.62 0.16 20);
--color-stk11-s2: #c98a30;
--color-stk11-s2: oklch(0.64 0.15 60);
--color-stk11-s3: #45a878;
--color-stk11-s3: oklch(0.64 0.15 150);
--color-stk11-s4: #4a86cf;
--color-stk11-s4: oklch(0.60 0.16 235);
--color-stk11-s5: #9468cc;
--color-stk11-s5: oklch(0.60 0.16 300);
--font-stk11-sans: 'Archivo', system-ui, sans-serif;
--ease-stk11: 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,800&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,800&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 port's one genuine cost is visible here: an arbitrary
transformreplaces the whole property, sorotate(var(--a))has to be restated in every state that builds on it. transform-originships a named scale but nothing at50% 130%, so the below-the-card anchor stays arbitrary — and it is the declaration the whole fan depends on.- The focus ring is a spread
box-shadowrather than anoutline, because it has to hug a rotated card and stack with the existing drop shadow in one value. motion-reduce:hover:[transform:…]keeps the raise and drops the scale — two variants deep, which is the ordinary way to express a calmed-down state inline.- Card faces are real
<button>s, so the whole hand is keyboard-operable with notabindexand no key handling. - The stylesheet is
@themeonly: five suit colours, a felt pair, a focus ring and one easing curve.
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 Fan Spread Hand of Cards
Source: https://codefronts.com/components/tailwind-stacked-cards/tailwind-fan-spread-hand-of-cards/
A poker hand, fanned from one anchor. Each card carries its own --a and every state repeats that angle: rest is [transform:rotate(var(--a))], raised is hover:[transform:rotate(var(--a))_translateY(-40px)_scale(1.05)]. Because the pivot is shared and low, spacing stays even however many cards you deal — adding a sixth is one angle, not a new rule.
## HTML
```html
<section class="stk-11 w-full min-h-svh flex items-end justify-center px-5 pt-15 pb-[22vh] font-stk11-sans bg-stk11-felt [background-image:radial-gradient(circle_at_50%_120%,var(--color-stk11-glow),transparent_70%)]" aria-label="Fanned hand of cards">
<div class="relative w-50 h-50">
<button class="absolute left-1/2 bottom-0 -ml-15 flex h-42.5 w-30 flex-col items-start justify-between border-0 cursor-pointer rounded-[14px] bg-white px-4 py-3.5 text-3xl font-extrabold text-stk11-s1 shadow-[0_14px_30px_-14px_rgba(0,0,0,0.7)] [transform-origin:50%_130%] [transform:rotate(var(--a))] transition-[transform,box-shadow] duration-350 ease-stk11 hover:z-10 hover:shadow-[0_26px_44px_-18px_rgba(0,0,0,0.8)] hover:[transform:rotate(var(--a))_translateY(-40px)_scale(1.05)] focus-visible:z-10 focus-visible:outline-none focus-visible:shadow-[0_0_0_3px_var(--color-stk11-ring),0_26px_44px_-18px_rgba(0,0,0,0.8)] focus-visible:[transform:rotate(var(--a))_translateY(-40px)_scale(1.05)] motion-reduce:hover:[transform:rotate(var(--a))_translateY(-40px)] motion-reduce:focus-visible:[transform:rotate(var(--a))_translateY(-40px)]" type="button" style="--a:-10deg">A<small class="self-end text-[28px]">♠</small></button>
<button class="absolute left-1/2 bottom-0 -ml-15 flex h-42.5 w-30 flex-col items-start justify-between border-0 cursor-pointer rounded-[14px] bg-white px-4 py-3.5 text-3xl font-extrabold text-stk11-s2 shadow-[0_14px_30px_-14px_rgba(0,0,0,0.7)] [transform-origin:50%_130%] [transform:rotate(var(--a))] transition-[transform,box-shadow] duration-350 ease-stk11 hover:z-10 hover:shadow-[0_26px_44px_-18px_rgba(0,0,0,0.8)] hover:[transform:rotate(var(--a))_translateY(-40px)_scale(1.05)] focus-visible:z-10 focus-visible:outline-none focus-visible:shadow-[0_0_0_3px_var(--color-stk11-ring),0_26px_44px_-18px_rgba(0,0,0,0.8)] focus-visible:[transform:rotate(var(--a))_translateY(-40px)_scale(1.05)] motion-reduce:hover:[transform:rotate(var(--a))_translateY(-40px)] motion-reduce:focus-visible:[transform:rotate(var(--a))_translateY(-40px)]" type="button" style="--a:-5deg">K<small class="self-end text-[28px]">♥</small></button>
<button class="absolute left-1/2 bottom-0 -ml-15 flex h-42.5 w-30 flex-col items-start justify-between border-0 cursor-pointer rounded-[14px] bg-white px-4 py-3.5 text-3xl font-extrabold text-stk11-s3 shadow-[0_14px_30px_-14px_rgba(0,0,0,0.7)] [transform-origin:50%_130%] [transform:rotate(var(--a))] transition-[transform,box-shadow] duration-350 ease-stk11 hover:z-10 hover:shadow-[0_26px_44px_-18px_rgba(0,0,0,0.8)] hover:[transform:rotate(var(--a))_translateY(-40px)_scale(1.05)] focus-visible:z-10 focus-visible:outline-none focus-visible:shadow-[0_0_0_3px_var(--color-stk11-ring),0_26px_44px_-18px_rgba(0,0,0,0.8)] focus-visible:[transform:rotate(var(--a))_translateY(-40px)_scale(1.05)] motion-reduce:hover:[transform:rotate(var(--a))_translateY(-40px)] motion-reduce:focus-visible:[transform:rotate(var(--a))_translateY(-40px)]" type="button" style="--a:0deg">Q<small class="self-end text-[28px]">♦</small></button>
<button class="absolute left-1/2 bottom-0 -ml-15 flex h-42.5 w-30 flex-col items-start justify-between border-0 cursor-pointer rounded-[14px] bg-white px-4 py-3.5 text-3xl font-extrabold text-stk11-s4 shadow-[0_14px_30px_-14px_rgba(0,0,0,0.7)] [transform-origin:50%_130%] [transform:rotate(var(--a))] transition-[transform,box-shadow] duration-350 ease-stk11 hover:z-10 hover:shadow-[0_26px_44px_-18px_rgba(0,0,0,0.8)] hover:[transform:rotate(var(--a))_translateY(-40px)_scale(1.05)] focus-visible:z-10 focus-visible:outline-none focus-visible:shadow-[0_0_0_3px_var(--color-stk11-ring),0_26px_44px_-18px_rgba(0,0,0,0.8)] focus-visible:[transform:rotate(var(--a))_translateY(-40px)_scale(1.05)] motion-reduce:hover:[transform:rotate(var(--a))_translateY(-40px)] motion-reduce:focus-visible:[transform:rotate(var(--a))_translateY(-40px)]" type="button" style="--a:5deg">J<small class="self-end text-[28px]">♣</small></button>
<button class="absolute left-1/2 bottom-0 -ml-15 flex h-42.5 w-30 flex-col items-start justify-between border-0 cursor-pointer rounded-[14px] bg-white px-4 py-3.5 text-3xl font-extrabold text-stk11-s5 shadow-[0_14px_30px_-14px_rgba(0,0,0,0.7)] [transform-origin:50%_130%] [transform:rotate(var(--a))] transition-[transform,box-shadow] duration-350 ease-stk11 hover:z-10 hover:shadow-[0_26px_44px_-18px_rgba(0,0,0,0.8)] hover:[transform:rotate(var(--a))_translateY(-40px)_scale(1.05)] focus-visible:z-10 focus-visible:outline-none focus-visible:shadow-[0_0_0_3px_var(--color-stk11-ring),0_26px_44px_-18px_rgba(0,0,0,0.8)] focus-visible:[transform:rotate(var(--a))_translateY(-40px)_scale(1.05)] motion-reduce:hover:[transform:rotate(var(--a))_translateY(-40px)] motion-reduce:focus-visible:[transform:rotate(var(--a))_translateY(-40px)]" type="button" style="--a:10deg">10<small class="self-end text-[28px]">★</small></button>
</div>
</section>
```
## CSS
```css
@import "tailwindcss";
@theme {
--color-stk11-felt: #0d281c;
--color-stk11-felt: oklch(0.26 0.055 155);
--color-stk11-glow: #1f5136;
--color-stk11-glow: oklch(0.38 0.075 155);
--color-stk11-ring: #e6c65a;
--color-stk11-ring: oklch(0.83 0.14 90);
--color-stk11-s1: #cf5f4b;
--color-stk11-s1: oklch(0.62 0.16 20);
--color-stk11-s2: #c98a30;
--color-stk11-s2: oklch(0.64 0.15 60);
--color-stk11-s3: #45a878;
--color-stk11-s3: oklch(0.64 0.15 150);
--color-stk11-s4: #4a86cf;
--color-stk11-s4: oklch(0.60 0.16 235);
--color-stk11-s5: #9468cc;
--color-stk11-s5: oklch(0.60 0.16 300);
--font-stk11-sans: 'Archivo', system-ui, sans-serif;
--ease-stk11: 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 Fan Spread Hand of Cards
Source: https://codefronts.com/components/tailwind-stacked-cards/tailwind-fan-spread-hand-of-cards/
A poker hand, fanned from one anchor. Each card carries its own --a and every state repeats that angle: rest is [transform:rotate(var(--a))], raised is hover:[transform:rotate(var(--a))_translateY(-40px)_scale(1.05)]. Because the pivot is shared and low, spacing stays even however many cards you deal — adding a sixth is one angle, not a new rule.
## HTML
```html
<section class="stk-11 w-full min-h-svh flex items-end justify-center px-5 pt-15 pb-[22vh] font-stk11-sans bg-stk11-felt [background-image:radial-gradient(circle_at_50%_120%,var(--color-stk11-glow),transparent_70%)]" aria-label="Fanned hand of cards">
<div class="relative w-50 h-50">
<button class="absolute left-1/2 bottom-0 -ml-15 flex h-42.5 w-30 flex-col items-start justify-between border-0 cursor-pointer rounded-[14px] bg-white px-4 py-3.5 text-3xl font-extrabold text-stk11-s1 shadow-[0_14px_30px_-14px_rgba(0,0,0,0.7)] [transform-origin:50%_130%] [transform:rotate(var(--a))] transition-[transform,box-shadow] duration-350 ease-stk11 hover:z-10 hover:shadow-[0_26px_44px_-18px_rgba(0,0,0,0.8)] hover:[transform:rotate(var(--a))_translateY(-40px)_scale(1.05)] focus-visible:z-10 focus-visible:outline-none focus-visible:shadow-[0_0_0_3px_var(--color-stk11-ring),0_26px_44px_-18px_rgba(0,0,0,0.8)] focus-visible:[transform:rotate(var(--a))_translateY(-40px)_scale(1.05)] motion-reduce:hover:[transform:rotate(var(--a))_translateY(-40px)] motion-reduce:focus-visible:[transform:rotate(var(--a))_translateY(-40px)]" type="button" style="--a:-10deg">A<small class="self-end text-[28px]">♠</small></button>
<button class="absolute left-1/2 bottom-0 -ml-15 flex h-42.5 w-30 flex-col items-start justify-between border-0 cursor-pointer rounded-[14px] bg-white px-4 py-3.5 text-3xl font-extrabold text-stk11-s2 shadow-[0_14px_30px_-14px_rgba(0,0,0,0.7)] [transform-origin:50%_130%] [transform:rotate(var(--a))] transition-[transform,box-shadow] duration-350 ease-stk11 hover:z-10 hover:shadow-[0_26px_44px_-18px_rgba(0,0,0,0.8)] hover:[transform:rotate(var(--a))_translateY(-40px)_scale(1.05)] focus-visible:z-10 focus-visible:outline-none focus-visible:shadow-[0_0_0_3px_var(--color-stk11-ring),0_26px_44px_-18px_rgba(0,0,0,0.8)] focus-visible:[transform:rotate(var(--a))_translateY(-40px)_scale(1.05)] motion-reduce:hover:[transform:rotate(var(--a))_translateY(-40px)] motion-reduce:focus-visible:[transform:rotate(var(--a))_translateY(-40px)]" type="button" style="--a:-5deg">K<small class="self-end text-[28px]">♥</small></button>
<button class="absolute left-1/2 bottom-0 -ml-15 flex h-42.5 w-30 flex-col items-start justify-between border-0 cursor-pointer rounded-[14px] bg-white px-4 py-3.5 text-3xl font-extrabold text-stk11-s3 shadow-[0_14px_30px_-14px_rgba(0,0,0,0.7)] [transform-origin:50%_130%] [transform:rotate(var(--a))] transition-[transform,box-shadow] duration-350 ease-stk11 hover:z-10 hover:shadow-[0_26px_44px_-18px_rgba(0,0,0,0.8)] hover:[transform:rotate(var(--a))_translateY(-40px)_scale(1.05)] focus-visible:z-10 focus-visible:outline-none focus-visible:shadow-[0_0_0_3px_var(--color-stk11-ring),0_26px_44px_-18px_rgba(0,0,0,0.8)] focus-visible:[transform:rotate(var(--a))_translateY(-40px)_scale(1.05)] motion-reduce:hover:[transform:rotate(var(--a))_translateY(-40px)] motion-reduce:focus-visible:[transform:rotate(var(--a))_translateY(-40px)]" type="button" style="--a:0deg">Q<small class="self-end text-[28px]">♦</small></button>
<button class="absolute left-1/2 bottom-0 -ml-15 flex h-42.5 w-30 flex-col items-start justify-between border-0 cursor-pointer rounded-[14px] bg-white px-4 py-3.5 text-3xl font-extrabold text-stk11-s4 shadow-[0_14px_30px_-14px_rgba(0,0,0,0.7)] [transform-origin:50%_130%] [transform:rotate(var(--a))] transition-[transform,box-shadow] duration-350 ease-stk11 hover:z-10 hover:shadow-[0_26px_44px_-18px_rgba(0,0,0,0.8)] hover:[transform:rotate(var(--a))_translateY(-40px)_scale(1.05)] focus-visible:z-10 focus-visible:outline-none focus-visible:shadow-[0_0_0_3px_var(--color-stk11-ring),0_26px_44px_-18px_rgba(0,0,0,0.8)] focus-visible:[transform:rotate(var(--a))_translateY(-40px)_scale(1.05)] motion-reduce:hover:[transform:rotate(var(--a))_translateY(-40px)] motion-reduce:focus-visible:[transform:rotate(var(--a))_translateY(-40px)]" type="button" style="--a:5deg">J<small class="self-end text-[28px]">♣</small></button>
<button class="absolute left-1/2 bottom-0 -ml-15 flex h-42.5 w-30 flex-col items-start justify-between border-0 cursor-pointer rounded-[14px] bg-white px-4 py-3.5 text-3xl font-extrabold text-stk11-s5 shadow-[0_14px_30px_-14px_rgba(0,0,0,0.7)] [transform-origin:50%_130%] [transform:rotate(var(--a))] transition-[transform,box-shadow] duration-350 ease-stk11 hover:z-10 hover:shadow-[0_26px_44px_-18px_rgba(0,0,0,0.8)] hover:[transform:rotate(var(--a))_translateY(-40px)_scale(1.05)] focus-visible:z-10 focus-visible:outline-none focus-visible:shadow-[0_0_0_3px_var(--color-stk11-ring),0_26px_44px_-18px_rgba(0,0,0,0.8)] focus-visible:[transform:rotate(var(--a))_translateY(-40px)_scale(1.05)] motion-reduce:hover:[transform:rotate(var(--a))_translateY(-40px)] motion-reduce:focus-visible:[transform:rotate(var(--a))_translateY(-40px)]" type="button" style="--a:10deg">10<small class="self-end text-[28px]">★</small></button>
</div>
</section>
```
## CSS
```css
@import "tailwindcss";
@theme {
--color-stk11-felt: #0d281c;
--color-stk11-felt: oklch(0.26 0.055 155);
--color-stk11-glow: #1f5136;
--color-stk11-glow: oklch(0.38 0.075 155);
--color-stk11-ring: #e6c65a;
--color-stk11-ring: oklch(0.83 0.14 90);
--color-stk11-s1: #cf5f4b;
--color-stk11-s1: oklch(0.62 0.16 20);
--color-stk11-s2: #c98a30;
--color-stk11-s2: oklch(0.64 0.15 60);
--color-stk11-s3: #45a878;
--color-stk11-s3: oklch(0.64 0.15 150);
--color-stk11-s4: #4a86cf;
--color-stk11-s4: oklch(0.60 0.16 235);
--color-stk11-s5: #9468cc;
--color-stk11-s5: oklch(0.60 0.16 300);
--font-stk11-sans: 'Archivo', system-ui, sans-serif;
--ease-stk11: cubic-bezier(0.3, 0.9, 0.3, 1);
}
```How this works
The angle is the card's own property. One number per card, reused in three states:
<button style="--a:-10deg"
class="[transform-origin:50%_130%] [transform:rotate(var(--a))]
hover:[transform:rotate(var(--a))_translateY(-40px)_scale(1.05)]
focus-visible:[transform:rotate(var(--a))_translateY(-40px)_scale(1.05)]">The repetition is deliberate and unavoidable: an arbitrary transform replaces the whole property, so every state has to restate the rotation it is building on. That is the one real ergonomic cost of porting transform-heavy CSS to utilities, and it is worth knowing before you hit it.
The shared pivot does the fanning. All five cards anchor at 50% 130% — a point below every card — so equal angle steps produce equal visual spacing. Anchor them at their own centres instead and the same angles scatter the hand.
Focus rings that rotate. An outline is drawn in the element's own box but does not follow a transform's perspective as cleanly as a spread shadow does; focus-visible:shadow-[0_0_0_3px_var(--color-stk11-ring)] hugs the rotated card exactly, and stacks with the drop shadow in the same value.
Make it yours
- Set the spread by changing the
--aincrement — 5deg is a tight hand, 10deg is a showman's. - Move the pivot with
[transform-origin:50%_130%]: closer to the card widens the fan for the same angles. - Deal more cards by adding buttons with the next angles; the arithmetic is per-card, so nothing needs renumbering.
- Change the raise height in the
hover:andfocus-visible:transforms — keep them identical or the keyboard path drifts from the pointer one. - Restyle the faces: the suits are plain text, so swap them for photos, logos or numerals.
Gotchas — read before shipping
- Every state's transform must restate
rotate(var(--a)). Arbitrary transforms replace, not compose — drop it and the card snaps upright as it lifts. - Anchor all cards to the same origin or the fan looks lopsided however carefully you pick the angles.
- Bump
z-10on hover and focus. Without it a raised card can still sit behind its neighbour. - Give the stage bottom padding — the fan's outer cards swing wide of the container's box.
- Keep
hover:andfocus-visible:in sync. Two slightly different raises is a bug that only keyboard users see. - Do not put the ring on an
outlinehere; a spread shadow tracks the rotated card more accurately.
Browser support
| Chrome | Safari | Firefox | Edge |
|---|---|---|---|
| 111+ | 16.4+ | 128+ | 111+ |
transform-origin with custom properties in transforms is universal (Chrome 49+, Safari 10+, Firefox 52+). The floor is Tailwind v4's own generated sheet.