12 / 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 Polaroid Photo Stack Scattered Rotate
Snapshots tossed on a table: thick white frames, handwritten captions, deliberate mess. Each snap's position is three numbers on the element (--x, --y, --r) and hovering zeroes the rotation while raising z-10, so the one you are looking at pops clean out of the pile. The captions carry a --font-* theme entry, which is how a second typeface becomes font-stk12-hand rather than a one-off 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-12 w-full min-h-svh flex items-center justify-center px-5 py-15 font-stk12-sans bg-stk12-table" aria-label="Scattered Polaroid photos">
<div class="relative w-[min(360px,90vw)] h-85">
<figure class="absolute left-1/2 top-1/2 -ml-22.5 -mt-27.5 w-45 cursor-pointer rounded-[3px] bg-white p-3 pb-0 shadow-[0_16px_30px_-14px_rgba(40,30,10,0.6)] [transform:translate(var(--x),var(--y))_rotate(var(--r))] transition-[transform,box-shadow] duration-350 ease-stk12 hover:z-10 hover:shadow-[0_30px_50px_-18px_rgba(40,30,10,0.75)] hover:[transform:translate(var(--x),var(--y))_rotate(0deg)_scale(1.08)] focus-visible:z-10 focus-visible:outline-none focus-visible:shadow-[0_0_0_3px_var(--color-stk12-ring),0_30px_50px_-18px_rgba(40,30,10,0.75)] focus-visible:[transform:translate(var(--x),var(--y))_rotate(0deg)_scale(1.08)] motion-reduce:hover:[transform:translate(var(--x),var(--y))_rotate(0deg)] motion-reduce:focus-visible:[transform:translate(var(--x),var(--y))_rotate(0deg)]" tabindex="0" style="--x:-90px;--y:-30px;--r:-8deg">
<img class="block w-full h-37.5 object-cover" src="https://picsum.photos/seed/tw-pola-1/300/300" width="300" height="300" alt="Tent pitched beside a lake at dawn">
<figcaption class="px-1 pt-3 pb-3.5 text-center font-stk12-hand text-base text-stk12-ink">Dawn, day 3</figcaption>
</figure>
<figure class="absolute left-1/2 top-1/2 -ml-22.5 -mt-27.5 w-45 cursor-pointer rounded-[3px] bg-white p-3 pb-0 shadow-[0_16px_30px_-14px_rgba(40,30,10,0.6)] [transform:translate(var(--x),var(--y))_rotate(var(--r))] transition-[transform,box-shadow] duration-350 ease-stk12 hover:z-10 hover:shadow-[0_30px_50px_-18px_rgba(40,30,10,0.75)] hover:[transform:translate(var(--x),var(--y))_rotate(0deg)_scale(1.08)] focus-visible:z-10 focus-visible:outline-none focus-visible:shadow-[0_0_0_3px_var(--color-stk12-ring),0_30px_50px_-18px_rgba(40,30,10,0.75)] focus-visible:[transform:translate(var(--x),var(--y))_rotate(0deg)_scale(1.08)] motion-reduce:hover:[transform:translate(var(--x),var(--y))_rotate(0deg)] motion-reduce:focus-visible:[transform:translate(var(--x),var(--y))_rotate(0deg)]" tabindex="0" style="--x:30px;--y:-50px;--r:6deg">
<img class="block w-full h-37.5 object-cover" src="https://picsum.photos/seed/tw-pola-2/300/300" width="300" height="300" alt="Two enamel mugs of coffee on a camp stove">
<figcaption class="px-1 pt-3 pb-3.5 text-center font-stk12-hand text-base text-stk12-ink">First light coffee</figcaption>
</figure>
<figure class="absolute left-1/2 top-1/2 -ml-22.5 -mt-27.5 w-45 cursor-pointer rounded-[3px] bg-white p-3 pb-0 shadow-[0_16px_30px_-14px_rgba(40,30,10,0.6)] [transform:translate(var(--x),var(--y))_rotate(var(--r))] transition-[transform,box-shadow] duration-350 ease-stk12 hover:z-10 hover:shadow-[0_30px_50px_-18px_rgba(40,30,10,0.75)] hover:[transform:translate(var(--x),var(--y))_rotate(0deg)_scale(1.08)] focus-visible:z-10 focus-visible:outline-none focus-visible:shadow-[0_0_0_3px_var(--color-stk12-ring),0_30px_50px_-18px_rgba(40,30,10,0.75)] focus-visible:[transform:translate(var(--x),var(--y))_rotate(0deg)_scale(1.08)] motion-reduce:hover:[transform:translate(var(--x),var(--y))_rotate(0deg)] motion-reduce:focus-visible:[transform:translate(var(--x),var(--y))_rotate(0deg)]" tabindex="0" style="--x:-40px;--y:40px;--r:4deg">
<img class="block w-full h-37.5 object-cover" src="https://picsum.photos/seed/tw-pola-3/300/300" width="300" height="300" alt="Painted trail marker on a mountain path">
<figcaption class="px-1 pt-3 pb-3.5 text-center font-stk12-hand text-base text-stk12-ink">Km 14</figcaption>
</figure>
<figure class="absolute left-1/2 top-1/2 -ml-22.5 -mt-27.5 w-45 cursor-pointer rounded-[3px] bg-white p-3 pb-0 shadow-[0_16px_30px_-14px_rgba(40,30,10,0.6)] [transform:translate(var(--x),var(--y))_rotate(var(--r))] transition-[transform,box-shadow] duration-350 ease-stk12 hover:z-10 hover:shadow-[0_30px_50px_-18px_rgba(40,30,10,0.75)] hover:[transform:translate(var(--x),var(--y))_rotate(0deg)_scale(1.08)] focus-visible:z-10 focus-visible:outline-none focus-visible:shadow-[0_0_0_3px_var(--color-stk12-ring),0_30px_50px_-18px_rgba(40,30,10,0.75)] focus-visible:[transform:translate(var(--x),var(--y))_rotate(0deg)_scale(1.08)] motion-reduce:hover:[transform:translate(var(--x),var(--y))_rotate(0deg)] motion-reduce:focus-visible:[transform:translate(var(--x),var(--y))_rotate(0deg)]" tabindex="0" style="--x:80px;--y:30px;--r:-5deg">
<img class="block w-full h-37.5 object-cover" src="https://picsum.photos/seed/tw-pola-4/300/300" width="300" height="300" alt="Sun setting behind a long ridge">
<figcaption class="px-1 pt-3 pb-3.5 text-center font-stk12-hand text-base text-stk12-ink">Golden hour</figcaption>
</figure>
</div>
</section><section class="stk-12 w-full min-h-svh flex items-center justify-center px-5 py-15 font-stk12-sans bg-stk12-table" aria-label="Scattered Polaroid photos">
<div class="relative w-[min(360px,90vw)] h-85">
<figure class="absolute left-1/2 top-1/2 -ml-22.5 -mt-27.5 w-45 cursor-pointer rounded-[3px] bg-white p-3 pb-0 shadow-[0_16px_30px_-14px_rgba(40,30,10,0.6)] [transform:translate(var(--x),var(--y))_rotate(var(--r))] transition-[transform,box-shadow] duration-350 ease-stk12 hover:z-10 hover:shadow-[0_30px_50px_-18px_rgba(40,30,10,0.75)] hover:[transform:translate(var(--x),var(--y))_rotate(0deg)_scale(1.08)] focus-visible:z-10 focus-visible:outline-none focus-visible:shadow-[0_0_0_3px_var(--color-stk12-ring),0_30px_50px_-18px_rgba(40,30,10,0.75)] focus-visible:[transform:translate(var(--x),var(--y))_rotate(0deg)_scale(1.08)] motion-reduce:hover:[transform:translate(var(--x),var(--y))_rotate(0deg)] motion-reduce:focus-visible:[transform:translate(var(--x),var(--y))_rotate(0deg)]" tabindex="0" style="--x:-90px;--y:-30px;--r:-8deg">
<img class="block w-full h-37.5 object-cover" src="https://picsum.photos/seed/tw-pola-1/300/300" width="300" height="300" alt="Tent pitched beside a lake at dawn">
<figcaption class="px-1 pt-3 pb-3.5 text-center font-stk12-hand text-base text-stk12-ink">Dawn, day 3</figcaption>
</figure>
<figure class="absolute left-1/2 top-1/2 -ml-22.5 -mt-27.5 w-45 cursor-pointer rounded-[3px] bg-white p-3 pb-0 shadow-[0_16px_30px_-14px_rgba(40,30,10,0.6)] [transform:translate(var(--x),var(--y))_rotate(var(--r))] transition-[transform,box-shadow] duration-350 ease-stk12 hover:z-10 hover:shadow-[0_30px_50px_-18px_rgba(40,30,10,0.75)] hover:[transform:translate(var(--x),var(--y))_rotate(0deg)_scale(1.08)] focus-visible:z-10 focus-visible:outline-none focus-visible:shadow-[0_0_0_3px_var(--color-stk12-ring),0_30px_50px_-18px_rgba(40,30,10,0.75)] focus-visible:[transform:translate(var(--x),var(--y))_rotate(0deg)_scale(1.08)] motion-reduce:hover:[transform:translate(var(--x),var(--y))_rotate(0deg)] motion-reduce:focus-visible:[transform:translate(var(--x),var(--y))_rotate(0deg)]" tabindex="0" style="--x:30px;--y:-50px;--r:6deg">
<img class="block w-full h-37.5 object-cover" src="https://picsum.photos/seed/tw-pola-2/300/300" width="300" height="300" alt="Two enamel mugs of coffee on a camp stove">
<figcaption class="px-1 pt-3 pb-3.5 text-center font-stk12-hand text-base text-stk12-ink">First light coffee</figcaption>
</figure>
<figure class="absolute left-1/2 top-1/2 -ml-22.5 -mt-27.5 w-45 cursor-pointer rounded-[3px] bg-white p-3 pb-0 shadow-[0_16px_30px_-14px_rgba(40,30,10,0.6)] [transform:translate(var(--x),var(--y))_rotate(var(--r))] transition-[transform,box-shadow] duration-350 ease-stk12 hover:z-10 hover:shadow-[0_30px_50px_-18px_rgba(40,30,10,0.75)] hover:[transform:translate(var(--x),var(--y))_rotate(0deg)_scale(1.08)] focus-visible:z-10 focus-visible:outline-none focus-visible:shadow-[0_0_0_3px_var(--color-stk12-ring),0_30px_50px_-18px_rgba(40,30,10,0.75)] focus-visible:[transform:translate(var(--x),var(--y))_rotate(0deg)_scale(1.08)] motion-reduce:hover:[transform:translate(var(--x),var(--y))_rotate(0deg)] motion-reduce:focus-visible:[transform:translate(var(--x),var(--y))_rotate(0deg)]" tabindex="0" style="--x:-40px;--y:40px;--r:4deg">
<img class="block w-full h-37.5 object-cover" src="https://picsum.photos/seed/tw-pola-3/300/300" width="300" height="300" alt="Painted trail marker on a mountain path">
<figcaption class="px-1 pt-3 pb-3.5 text-center font-stk12-hand text-base text-stk12-ink">Km 14</figcaption>
</figure>
<figure class="absolute left-1/2 top-1/2 -ml-22.5 -mt-27.5 w-45 cursor-pointer rounded-[3px] bg-white p-3 pb-0 shadow-[0_16px_30px_-14px_rgba(40,30,10,0.6)] [transform:translate(var(--x),var(--y))_rotate(var(--r))] transition-[transform,box-shadow] duration-350 ease-stk12 hover:z-10 hover:shadow-[0_30px_50px_-18px_rgba(40,30,10,0.75)] hover:[transform:translate(var(--x),var(--y))_rotate(0deg)_scale(1.08)] focus-visible:z-10 focus-visible:outline-none focus-visible:shadow-[0_0_0_3px_var(--color-stk12-ring),0_30px_50px_-18px_rgba(40,30,10,0.75)] focus-visible:[transform:translate(var(--x),var(--y))_rotate(0deg)_scale(1.08)] motion-reduce:hover:[transform:translate(var(--x),var(--y))_rotate(0deg)] motion-reduce:focus-visible:[transform:translate(var(--x),var(--y))_rotate(0deg)]" tabindex="0" style="--x:80px;--y:30px;--r:-5deg">
<img class="block w-full h-37.5 object-cover" src="https://picsum.photos/seed/tw-pola-4/300/300" width="300" height="300" alt="Sun setting behind a long ridge">
<figcaption class="px-1 pt-3 pb-3.5 text-center font-stk12-hand text-base text-stk12-ink">Golden hour</figcaption>
</figure>
</div>
</section>@import "tailwindcss";
@theme {
--color-stk12-table: #cdbfa8;
--color-stk12-table: oklch(0.81 0.032 80);
--color-stk12-ink: #4a4030;
--color-stk12-ink: oklch(0.38 0.025 80);
--color-stk12-ring: #5b78e8;
--color-stk12-ring: oklch(0.60 0.17 262);
--font-stk12-sans: 'Archivo', system-ui, sans-serif;
--font-stk12-hand: 'Caveat', 'Segoe Script', cursive;
--ease-stk12: cubic-bezier(0.3, 0.9, 0.3, 1);
}@import "tailwindcss";
@theme {
--color-stk12-table: #cdbfa8;
--color-stk12-table: oklch(0.81 0.032 80);
--color-stk12-ink: #4a4030;
--color-stk12-ink: oklch(0.38 0.025 80);
--color-stk12-ring: #5b78e8;
--color-stk12-ring: oklch(0.60 0.17 262);
--font-stk12-sans: 'Archivo', system-ui, sans-serif;
--font-stk12-hand: 'Caveat', 'Segoe Script', cursive;
--ease-stk12: 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-stk12-table: #cdbfa8;
--color-stk12-table: oklch(0.81 0.032 80);
--color-stk12-ink: #4a4030;
--color-stk12-ink: oklch(0.38 0.025 80);
--color-stk12-ring: #5b78e8;
--color-stk12-ring: oklch(0.60 0.17 262);
--font-stk12-sans: 'Archivo', system-ui, sans-serif;
--font-stk12-hand: 'Caveat', 'Segoe Script', cursive;
--ease-stk12: cubic-bezier(0.3, 0.9, 0.3, 1);
}@import "tailwindcss";
@theme {
--color-stk12-table: #cdbfa8;
--color-stk12-table: oklch(0.81 0.032 80);
--color-stk12-ink: #4a4030;
--color-stk12-ink: oklch(0.38 0.025 80);
--color-stk12-ring: #5b78e8;
--color-stk12-ring: oklch(0.60 0.17 262);
--font-stk12-sans: 'Archivo', system-ui, sans-serif;
--font-stk12-hand: 'Caveat', 'Segoe Script', cursive;
--ease-stk12: 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&family=Caveat:wght@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=Archivo:wdth,wght@75..125,400&family=Caveat:wght@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 handwriting face becomes a
--font-*theme entry, sofont-stk12-handis applied like any other token instead of hiding in afigcaptionrule. - The scatter is three numbers per element and one shared arbitrary transform — the same pattern as demo 03, here with rotation as the thing hover cancels rather than adds.
- The hover state repeats the translate and zeroes only the rotate, which is what makes the snap read as lifted rather than relocated.
- The centring maths moves from a
margin:-110px 0 0 -90pxshorthand to explicit-ml-22.5 -mt-27.5, which is legible next to theleft-1/2 top-1/2it corrects for. Caveatreplaces the CSS version's'Bradley Hand','Segoe Script'local-font stack, so the caption looks the same on every platform rather than only on macOS.- The stylesheet is
@themeonly: two colours, a focus ring, two fonts and an 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 Polaroid Photo Stack Scattered Rotate
Source: https://codefronts.com/components/tailwind-stacked-cards/tailwind-polaroid-photo-stack-scattered-rotate/
Snapshots tossed on a table: thick white frames, handwritten captions, deliberate mess. Each snap's position is three numbers on the element (--x, --y, --r) and hovering zeroes the rotation while raising z-10, so the one you are looking at pops clean out of the pile. The captions carry a --font-* theme entry, which is how a second typeface becomes font-stk12-hand rather than a one-off rule.
## HTML
```html
<section class="stk-12 w-full min-h-svh flex items-center justify-center px-5 py-15 font-stk12-sans bg-stk12-table" aria-label="Scattered Polaroid photos">
<div class="relative w-[min(360px,90vw)] h-85">
<figure class="absolute left-1/2 top-1/2 -ml-22.5 -mt-27.5 w-45 cursor-pointer rounded-[3px] bg-white p-3 pb-0 shadow-[0_16px_30px_-14px_rgba(40,30,10,0.6)] [transform:translate(var(--x),var(--y))_rotate(var(--r))] transition-[transform,box-shadow] duration-350 ease-stk12 hover:z-10 hover:shadow-[0_30px_50px_-18px_rgba(40,30,10,0.75)] hover:[transform:translate(var(--x),var(--y))_rotate(0deg)_scale(1.08)] focus-visible:z-10 focus-visible:outline-none focus-visible:shadow-[0_0_0_3px_var(--color-stk12-ring),0_30px_50px_-18px_rgba(40,30,10,0.75)] focus-visible:[transform:translate(var(--x),var(--y))_rotate(0deg)_scale(1.08)] motion-reduce:hover:[transform:translate(var(--x),var(--y))_rotate(0deg)] motion-reduce:focus-visible:[transform:translate(var(--x),var(--y))_rotate(0deg)]" tabindex="0" style="--x:-90px;--y:-30px;--r:-8deg">
<img class="block w-full h-37.5 object-cover" src="https://picsum.photos/seed/tw-pola-1/300/300" width="300" height="300" alt="Tent pitched beside a lake at dawn">
<figcaption class="px-1 pt-3 pb-3.5 text-center font-stk12-hand text-base text-stk12-ink">Dawn, day 3</figcaption>
</figure>
<figure class="absolute left-1/2 top-1/2 -ml-22.5 -mt-27.5 w-45 cursor-pointer rounded-[3px] bg-white p-3 pb-0 shadow-[0_16px_30px_-14px_rgba(40,30,10,0.6)] [transform:translate(var(--x),var(--y))_rotate(var(--r))] transition-[transform,box-shadow] duration-350 ease-stk12 hover:z-10 hover:shadow-[0_30px_50px_-18px_rgba(40,30,10,0.75)] hover:[transform:translate(var(--x),var(--y))_rotate(0deg)_scale(1.08)] focus-visible:z-10 focus-visible:outline-none focus-visible:shadow-[0_0_0_3px_var(--color-stk12-ring),0_30px_50px_-18px_rgba(40,30,10,0.75)] focus-visible:[transform:translate(var(--x),var(--y))_rotate(0deg)_scale(1.08)] motion-reduce:hover:[transform:translate(var(--x),var(--y))_rotate(0deg)] motion-reduce:focus-visible:[transform:translate(var(--x),var(--y))_rotate(0deg)]" tabindex="0" style="--x:30px;--y:-50px;--r:6deg">
<img class="block w-full h-37.5 object-cover" src="https://picsum.photos/seed/tw-pola-2/300/300" width="300" height="300" alt="Two enamel mugs of coffee on a camp stove">
<figcaption class="px-1 pt-3 pb-3.5 text-center font-stk12-hand text-base text-stk12-ink">First light coffee</figcaption>
</figure>
<figure class="absolute left-1/2 top-1/2 -ml-22.5 -mt-27.5 w-45 cursor-pointer rounded-[3px] bg-white p-3 pb-0 shadow-[0_16px_30px_-14px_rgba(40,30,10,0.6)] [transform:translate(var(--x),var(--y))_rotate(var(--r))] transition-[transform,box-shadow] duration-350 ease-stk12 hover:z-10 hover:shadow-[0_30px_50px_-18px_rgba(40,30,10,0.75)] hover:[transform:translate(var(--x),var(--y))_rotate(0deg)_scale(1.08)] focus-visible:z-10 focus-visible:outline-none focus-visible:shadow-[0_0_0_3px_var(--color-stk12-ring),0_30px_50px_-18px_rgba(40,30,10,0.75)] focus-visible:[transform:translate(var(--x),var(--y))_rotate(0deg)_scale(1.08)] motion-reduce:hover:[transform:translate(var(--x),var(--y))_rotate(0deg)] motion-reduce:focus-visible:[transform:translate(var(--x),var(--y))_rotate(0deg)]" tabindex="0" style="--x:-40px;--y:40px;--r:4deg">
<img class="block w-full h-37.5 object-cover" src="https://picsum.photos/seed/tw-pola-3/300/300" width="300" height="300" alt="Painted trail marker on a mountain path">
<figcaption class="px-1 pt-3 pb-3.5 text-center font-stk12-hand text-base text-stk12-ink">Km 14</figcaption>
</figure>
<figure class="absolute left-1/2 top-1/2 -ml-22.5 -mt-27.5 w-45 cursor-pointer rounded-[3px] bg-white p-3 pb-0 shadow-[0_16px_30px_-14px_rgba(40,30,10,0.6)] [transform:translate(var(--x),var(--y))_rotate(var(--r))] transition-[transform,box-shadow] duration-350 ease-stk12 hover:z-10 hover:shadow-[0_30px_50px_-18px_rgba(40,30,10,0.75)] hover:[transform:translate(var(--x),var(--y))_rotate(0deg)_scale(1.08)] focus-visible:z-10 focus-visible:outline-none focus-visible:shadow-[0_0_0_3px_var(--color-stk12-ring),0_30px_50px_-18px_rgba(40,30,10,0.75)] focus-visible:[transform:translate(var(--x),var(--y))_rotate(0deg)_scale(1.08)] motion-reduce:hover:[transform:translate(var(--x),var(--y))_rotate(0deg)] motion-reduce:focus-visible:[transform:translate(var(--x),var(--y))_rotate(0deg)]" tabindex="0" style="--x:80px;--y:30px;--r:-5deg">
<img class="block w-full h-37.5 object-cover" src="https://picsum.photos/seed/tw-pola-4/300/300" width="300" height="300" alt="Sun setting behind a long ridge">
<figcaption class="px-1 pt-3 pb-3.5 text-center font-stk12-hand text-base text-stk12-ink">Golden hour</figcaption>
</figure>
</div>
</section>
```
## CSS
```css
@import "tailwindcss";
@theme {
--color-stk12-table: #cdbfa8;
--color-stk12-table: oklch(0.81 0.032 80);
--color-stk12-ink: #4a4030;
--color-stk12-ink: oklch(0.38 0.025 80);
--color-stk12-ring: #5b78e8;
--color-stk12-ring: oklch(0.60 0.17 262);
--font-stk12-sans: 'Archivo', system-ui, sans-serif;
--font-stk12-hand: 'Caveat', 'Segoe Script', cursive;
--ease-stk12: 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 Polaroid Photo Stack Scattered Rotate
Source: https://codefronts.com/components/tailwind-stacked-cards/tailwind-polaroid-photo-stack-scattered-rotate/
Snapshots tossed on a table: thick white frames, handwritten captions, deliberate mess. Each snap's position is three numbers on the element (--x, --y, --r) and hovering zeroes the rotation while raising z-10, so the one you are looking at pops clean out of the pile. The captions carry a --font-* theme entry, which is how a second typeface becomes font-stk12-hand rather than a one-off rule.
## HTML
```html
<section class="stk-12 w-full min-h-svh flex items-center justify-center px-5 py-15 font-stk12-sans bg-stk12-table" aria-label="Scattered Polaroid photos">
<div class="relative w-[min(360px,90vw)] h-85">
<figure class="absolute left-1/2 top-1/2 -ml-22.5 -mt-27.5 w-45 cursor-pointer rounded-[3px] bg-white p-3 pb-0 shadow-[0_16px_30px_-14px_rgba(40,30,10,0.6)] [transform:translate(var(--x),var(--y))_rotate(var(--r))] transition-[transform,box-shadow] duration-350 ease-stk12 hover:z-10 hover:shadow-[0_30px_50px_-18px_rgba(40,30,10,0.75)] hover:[transform:translate(var(--x),var(--y))_rotate(0deg)_scale(1.08)] focus-visible:z-10 focus-visible:outline-none focus-visible:shadow-[0_0_0_3px_var(--color-stk12-ring),0_30px_50px_-18px_rgba(40,30,10,0.75)] focus-visible:[transform:translate(var(--x),var(--y))_rotate(0deg)_scale(1.08)] motion-reduce:hover:[transform:translate(var(--x),var(--y))_rotate(0deg)] motion-reduce:focus-visible:[transform:translate(var(--x),var(--y))_rotate(0deg)]" tabindex="0" style="--x:-90px;--y:-30px;--r:-8deg">
<img class="block w-full h-37.5 object-cover" src="https://picsum.photos/seed/tw-pola-1/300/300" width="300" height="300" alt="Tent pitched beside a lake at dawn">
<figcaption class="px-1 pt-3 pb-3.5 text-center font-stk12-hand text-base text-stk12-ink">Dawn, day 3</figcaption>
</figure>
<figure class="absolute left-1/2 top-1/2 -ml-22.5 -mt-27.5 w-45 cursor-pointer rounded-[3px] bg-white p-3 pb-0 shadow-[0_16px_30px_-14px_rgba(40,30,10,0.6)] [transform:translate(var(--x),var(--y))_rotate(var(--r))] transition-[transform,box-shadow] duration-350 ease-stk12 hover:z-10 hover:shadow-[0_30px_50px_-18px_rgba(40,30,10,0.75)] hover:[transform:translate(var(--x),var(--y))_rotate(0deg)_scale(1.08)] focus-visible:z-10 focus-visible:outline-none focus-visible:shadow-[0_0_0_3px_var(--color-stk12-ring),0_30px_50px_-18px_rgba(40,30,10,0.75)] focus-visible:[transform:translate(var(--x),var(--y))_rotate(0deg)_scale(1.08)] motion-reduce:hover:[transform:translate(var(--x),var(--y))_rotate(0deg)] motion-reduce:focus-visible:[transform:translate(var(--x),var(--y))_rotate(0deg)]" tabindex="0" style="--x:30px;--y:-50px;--r:6deg">
<img class="block w-full h-37.5 object-cover" src="https://picsum.photos/seed/tw-pola-2/300/300" width="300" height="300" alt="Two enamel mugs of coffee on a camp stove">
<figcaption class="px-1 pt-3 pb-3.5 text-center font-stk12-hand text-base text-stk12-ink">First light coffee</figcaption>
</figure>
<figure class="absolute left-1/2 top-1/2 -ml-22.5 -mt-27.5 w-45 cursor-pointer rounded-[3px] bg-white p-3 pb-0 shadow-[0_16px_30px_-14px_rgba(40,30,10,0.6)] [transform:translate(var(--x),var(--y))_rotate(var(--r))] transition-[transform,box-shadow] duration-350 ease-stk12 hover:z-10 hover:shadow-[0_30px_50px_-18px_rgba(40,30,10,0.75)] hover:[transform:translate(var(--x),var(--y))_rotate(0deg)_scale(1.08)] focus-visible:z-10 focus-visible:outline-none focus-visible:shadow-[0_0_0_3px_var(--color-stk12-ring),0_30px_50px_-18px_rgba(40,30,10,0.75)] focus-visible:[transform:translate(var(--x),var(--y))_rotate(0deg)_scale(1.08)] motion-reduce:hover:[transform:translate(var(--x),var(--y))_rotate(0deg)] motion-reduce:focus-visible:[transform:translate(var(--x),var(--y))_rotate(0deg)]" tabindex="0" style="--x:-40px;--y:40px;--r:4deg">
<img class="block w-full h-37.5 object-cover" src="https://picsum.photos/seed/tw-pola-3/300/300" width="300" height="300" alt="Painted trail marker on a mountain path">
<figcaption class="px-1 pt-3 pb-3.5 text-center font-stk12-hand text-base text-stk12-ink">Km 14</figcaption>
</figure>
<figure class="absolute left-1/2 top-1/2 -ml-22.5 -mt-27.5 w-45 cursor-pointer rounded-[3px] bg-white p-3 pb-0 shadow-[0_16px_30px_-14px_rgba(40,30,10,0.6)] [transform:translate(var(--x),var(--y))_rotate(var(--r))] transition-[transform,box-shadow] duration-350 ease-stk12 hover:z-10 hover:shadow-[0_30px_50px_-18px_rgba(40,30,10,0.75)] hover:[transform:translate(var(--x),var(--y))_rotate(0deg)_scale(1.08)] focus-visible:z-10 focus-visible:outline-none focus-visible:shadow-[0_0_0_3px_var(--color-stk12-ring),0_30px_50px_-18px_rgba(40,30,10,0.75)] focus-visible:[transform:translate(var(--x),var(--y))_rotate(0deg)_scale(1.08)] motion-reduce:hover:[transform:translate(var(--x),var(--y))_rotate(0deg)] motion-reduce:focus-visible:[transform:translate(var(--x),var(--y))_rotate(0deg)]" tabindex="0" style="--x:80px;--y:30px;--r:-5deg">
<img class="block w-full h-37.5 object-cover" src="https://picsum.photos/seed/tw-pola-4/300/300" width="300" height="300" alt="Sun setting behind a long ridge">
<figcaption class="px-1 pt-3 pb-3.5 text-center font-stk12-hand text-base text-stk12-ink">Golden hour</figcaption>
</figure>
</div>
</section>
```
## CSS
```css
@import "tailwindcss";
@theme {
--color-stk12-table: #cdbfa8;
--color-stk12-table: oklch(0.81 0.032 80);
--color-stk12-ink: #4a4030;
--color-stk12-ink: oklch(0.38 0.025 80);
--color-stk12-ring: #5b78e8;
--color-stk12-ring: oklch(0.60 0.17 262);
--font-stk12-sans: 'Archivo', system-ui, sans-serif;
--font-stk12-hand: 'Caveat', 'Segoe Script', cursive;
--ease-stk12: cubic-bezier(0.3, 0.9, 0.3, 1);
}
```How this works
Three numbers, one transform. Every snap declares where it landed and how crooked it is:
<figure style="--x:-90px;--y:-30px;--r:-8deg"
class="[transform:translate(var(--x),var(--y))_rotate(var(--r))]
hover:[transform:translate(var(--x),var(--y))_rotate(0deg)_scale(1.08)]">The hover state keeps the position and cancels only the rotation, which is what makes the snap look picked up rather than moved.
The caption font is a token. Adding --font-stk12-hand to @theme mints a font-stk12-hand utility, so the handwritten face is applied the same way as any colour and can be swapped in one place. A one-off font-family in a rule would work and would also be the only styling in the demo not visible in the markup.
Alt text is not optional here. A scattered pile is a decorative arrangement of real content: every photo keeps meaningful alt text and a visible caption, and the mess is styling that assistive technology never sees.
Make it yours
- Reposition a snap by editing its three numbers; nothing else in the demo knows or cares where the others are.
- Thicken the frame with the asymmetric padding (
p-3 pb-0plus the caption's own padding) — that imbalance is what makes it read as a Polaroid. - Swap the caption face by changing
--font-stk12-handin@theme. - Change the photo crop with the image's fixed
h-37.5andobject-cover. - Add tape or texture with a
before:utility on the figure rather than a new element.
Gotchas — read before shipping
- Keep meaningful
alttext. The scatter must not become a reason for the content to disappear from the accessibility tree. - The hovered snap needs
z-10; without it a later sibling still paints on top of the card you just straightened. - Give the board padding — rotated corners swing outside the unrotated box and clip against a tight parent.
- The hover transform has to repeat
translate(var(--x),var(--y)), or straightening also throws the snap back to the centre. - Set an explicit image height and
object-cover. Intrinsic-ratio images inside a fixed frame stretch the white border unevenly. - Handwriting faces are often thin; keep the caption at
text-baseand full-strength ink so it still clears 4.5:1.
Browser support
| Chrome | Safari | Firefox | Edge |
|---|---|---|---|
| 111+ | 16.4+ | 128+ | 111+ |
Absolute positioning, transforms and object-fit are universal (Chrome 49+, Safari 10+, Firefox 52+). The floor is Tailwind v4's own generated sheet.