18 / 22
All 22 in 22 Tailwind CSS Image Gallery
- 01Tailwind Hexagon Honeycomb Image Gallery with clip-path
- 02Tailwind Justified Image Gallery with Aspect-Ratio Flex Rows
- 03Tailwind Expanding Panel Image Gallery with Flex-Grow Transitions
- 04Tailwind :has() Hover Spotlight Image Grid
- 05Tailwind Radial Orbit Image Gallery with Counter-Rotating Thumbnails
- 06Tailwind Interactive Pointer Trail Image Gallery
- 07Tailwind Masonry Image Gallery with CSS Columns (No JavaScript)
- 08Tailwind Metro Tile Grid Gallery with Dense Auto-Flow
- 09Tailwind Editorial Magazine Grid Gallery with Blend-Mode Headline
- 10Tailwind Pure CSS Lightbox Gallery with :target (No JavaScript)
- 11Tailwind Filmstrip Carousel Gallery with CSS Scroll-Snap
- 12Tailwind Accordion Image Gallery with Flex-Grow on Hover & Focus
- 13Tailwind Mosaic Grid Gallery with Contained Zoom on Hover
- 14Tailwind Glassmorphism Image Cards with backdrop-blur
- 15Tailwind Diagonal Slice Reveal Gallery with clip-path Halves
- 16Tailwind 3D Tilt Parallax Image Cards with Pointer-Driven Variables
- 17Tailwind 3D Cube Image Viewer with Radio-Driven Rotation
- 18Tailwind 3D Perspective Depth Stack Gallery That Fans on Focus
- 19Tailwind Infinite Scrolling Image Marquee with Seamless Loop
- 20Tailwind Ken Burns Slideshow Gallery with Staggered Cross-Fade
- 21Tailwind Polaroid Photo Stack Gallery with Rotate-on-Hover
- 22Tailwind Scattered Photo Collage with :target Spotlight
Tailwind 3D Perspective Depth Stack Gallery That Fans on Focus
A deck of photographs stacked in 3D that fans into a cascade when you hover or focus it. Each card's resting depth and fanned position are two utilities on the card itself, which replaces four nth-child rules with markup you can reorder freely.
Untitled UI Pro: 5,000+ React + Tailwind CSS v4 components · from $349 one-time
Untitled UI React · Affiliate link
The code
<section class="tgl-18 w-full min-h-svh flex flex-col items-center justify-center gap-7 bg-tgl18-bg font-tgl18-sans px-5 py-10" aria-label="Perspective depth stack gallery">
<div class="group relative w-[260px] h-[330px] [perspective:1200px]">
<a href="#" class="absolute inset-0 rounded-2xl overflow-hidden transform-3d origin-bottom shadow-[0_20px_40px_-20px_rgba(0,0,0,0.7)] [transform:translateZ(60px)] transition-transform duration-[550ms] ease-[cubic-bezier(0.2,0.7,0.2,1)] group-hover:[transform:translateX(-150px)_rotate(-9deg)] group-focus-within:[transform:translateX(-150px)_rotate(-9deg)] focus-visible:outline-3 focus-visible:outline-tgl18-acc focus-visible:outline-offset-[3px] focus-visible:z-[5] motion-reduce:transition-none">
<img src="https://images.unsplash.com/photo-1607746882042-944635dfe10e?q=80&w=500&h=620&auto=format&fit=crop" alt="Studio portrait in high contrast" loading="lazy" decoding="async" class="block w-full h-full object-cover">
</a>
<a href="#" class="absolute inset-0 rounded-2xl overflow-hidden transform-3d origin-bottom shadow-[0_20px_40px_-20px_rgba(0,0,0,0.7)] [transform:translateZ(40px)_translateY(10px)_scale(0.97)] transition-transform duration-[550ms] ease-[cubic-bezier(0.2,0.7,0.2,1)] group-hover:[transform:translateX(-50px)_rotate(-3deg)] group-focus-within:[transform:translateX(-50px)_rotate(-3deg)] focus-visible:outline-3 focus-visible:outline-tgl18-acc focus-visible:outline-offset-[3px] focus-visible:z-[5] motion-reduce:transition-none">
<img src="https://images.unsplash.com/photo-1505691938895-1758d7feb511?q=80&w=500&h=620&auto=format&fit=crop" alt="Minimal landscape study" loading="lazy" decoding="async" class="block w-full h-full object-cover">
</a>
<a href="#" class="absolute inset-0 rounded-2xl overflow-hidden transform-3d origin-bottom shadow-[0_20px_40px_-20px_rgba(0,0,0,0.7)] [transform:translateZ(20px)_translateY(20px)_scale(0.94)] transition-transform duration-[550ms] ease-[cubic-bezier(0.2,0.7,0.2,1)] group-hover:[transform:translateX(50px)_rotate(3deg)] group-focus-within:[transform:translateX(50px)_rotate(3deg)] focus-visible:outline-3 focus-visible:outline-tgl18-acc focus-visible:outline-offset-[3px] focus-visible:z-[5] motion-reduce:transition-none">
<img src="https://images.unsplash.com/photo-1470252649378-9c29740c9fa8?q=80&w=500&h=620&auto=format&fit=crop" alt="Street scene in late light" loading="lazy" decoding="async" class="block w-full h-full object-cover">
</a>
<a href="#" class="absolute inset-0 rounded-2xl overflow-hidden transform-3d origin-bottom shadow-[0_20px_40px_-20px_rgba(0,0,0,0.7)] [transform:translateZ(0)_translateY(30px)_scale(0.91)] transition-transform duration-[550ms] ease-[cubic-bezier(0.2,0.7,0.2,1)] group-hover:[transform:translateX(150px)_rotate(9deg)] group-focus-within:[transform:translateX(150px)_rotate(9deg)] focus-visible:outline-3 focus-visible:outline-tgl18-acc focus-visible:outline-offset-[3px] focus-visible:z-[5] motion-reduce:transition-none">
<img src="https://images.unsplash.com/photo-1450101499163-c8848c66ca85?q=80&w=500&h=620&auto=format&fit=crop" alt="Botanical detail on black" loading="lazy" decoding="async" class="block w-full h-full object-cover">
</a>
</div>
<p class="font-tgl18-mono text-[11px] tracking-[0.16em] uppercase text-tgl18-mut m-0">hover or focus the stack to fan it out</p>
</section><section class="tgl-18 w-full min-h-svh flex flex-col items-center justify-center gap-7 bg-tgl18-bg font-tgl18-sans px-5 py-10" aria-label="Perspective depth stack gallery">
<div class="group relative w-[260px] h-[330px] [perspective:1200px]">
<a href="#" class="absolute inset-0 rounded-2xl overflow-hidden transform-3d origin-bottom shadow-[0_20px_40px_-20px_rgba(0,0,0,0.7)] [transform:translateZ(60px)] transition-transform duration-[550ms] ease-[cubic-bezier(0.2,0.7,0.2,1)] group-hover:[transform:translateX(-150px)_rotate(-9deg)] group-focus-within:[transform:translateX(-150px)_rotate(-9deg)] focus-visible:outline-3 focus-visible:outline-tgl18-acc focus-visible:outline-offset-[3px] focus-visible:z-[5] motion-reduce:transition-none">
<img src="https://images.unsplash.com/photo-1607746882042-944635dfe10e?q=80&w=500&h=620&auto=format&fit=crop" alt="Studio portrait in high contrast" loading="lazy" decoding="async" class="block w-full h-full object-cover">
</a>
<a href="#" class="absolute inset-0 rounded-2xl overflow-hidden transform-3d origin-bottom shadow-[0_20px_40px_-20px_rgba(0,0,0,0.7)] [transform:translateZ(40px)_translateY(10px)_scale(0.97)] transition-transform duration-[550ms] ease-[cubic-bezier(0.2,0.7,0.2,1)] group-hover:[transform:translateX(-50px)_rotate(-3deg)] group-focus-within:[transform:translateX(-50px)_rotate(-3deg)] focus-visible:outline-3 focus-visible:outline-tgl18-acc focus-visible:outline-offset-[3px] focus-visible:z-[5] motion-reduce:transition-none">
<img src="https://images.unsplash.com/photo-1505691938895-1758d7feb511?q=80&w=500&h=620&auto=format&fit=crop" alt="Minimal landscape study" loading="lazy" decoding="async" class="block w-full h-full object-cover">
</a>
<a href="#" class="absolute inset-0 rounded-2xl overflow-hidden transform-3d origin-bottom shadow-[0_20px_40px_-20px_rgba(0,0,0,0.7)] [transform:translateZ(20px)_translateY(20px)_scale(0.94)] transition-transform duration-[550ms] ease-[cubic-bezier(0.2,0.7,0.2,1)] group-hover:[transform:translateX(50px)_rotate(3deg)] group-focus-within:[transform:translateX(50px)_rotate(3deg)] focus-visible:outline-3 focus-visible:outline-tgl18-acc focus-visible:outline-offset-[3px] focus-visible:z-[5] motion-reduce:transition-none">
<img src="https://images.unsplash.com/photo-1470252649378-9c29740c9fa8?q=80&w=500&h=620&auto=format&fit=crop" alt="Street scene in late light" loading="lazy" decoding="async" class="block w-full h-full object-cover">
</a>
<a href="#" class="absolute inset-0 rounded-2xl overflow-hidden transform-3d origin-bottom shadow-[0_20px_40px_-20px_rgba(0,0,0,0.7)] [transform:translateZ(0)_translateY(30px)_scale(0.91)] transition-transform duration-[550ms] ease-[cubic-bezier(0.2,0.7,0.2,1)] group-hover:[transform:translateX(150px)_rotate(9deg)] group-focus-within:[transform:translateX(150px)_rotate(9deg)] focus-visible:outline-3 focus-visible:outline-tgl18-acc focus-visible:outline-offset-[3px] focus-visible:z-[5] motion-reduce:transition-none">
<img src="https://images.unsplash.com/photo-1450101499163-c8848c66ca85?q=80&w=500&h=620&auto=format&fit=crop" alt="Botanical detail on black" loading="lazy" decoding="async" class="block w-full h-full object-cover">
</a>
</div>
<p class="font-tgl18-mono text-[11px] tracking-[0.16em] uppercase text-tgl18-mut m-0">hover or focus the stack to fan it out</p>
</section>@import "tailwindcss";
@theme {
--color-tgl18-bg: #14101a;
--color-tgl18-bg: oklch(0.18 0.018 310);
--color-tgl18-mut: #a396ad;
--color-tgl18-mut: oklch(0.72 0.02 315);
--color-tgl18-acc: #d06fc8;
--color-tgl18-acc: oklch(0.7 0.17 330);
--font-tgl18-sans: 'Space Grotesk', system-ui, sans-serif;
--font-tgl18-mono: 'JetBrains Mono', ui-monospace, Menlo, monospace;
}
.tgl-18 {
width: 100%;
min-height: 100svh;
}
.tgl-18 ::selection {
background: var(--color-tgl18-acc);
color: #fff;
}@import "tailwindcss";
@theme {
--color-tgl18-bg: #14101a;
--color-tgl18-bg: oklch(0.18 0.018 310);
--color-tgl18-mut: #a396ad;
--color-tgl18-mut: oklch(0.72 0.02 315);
--color-tgl18-acc: #d06fc8;
--color-tgl18-acc: oklch(0.7 0.17 330);
--font-tgl18-sans: 'Space Grotesk', system-ui, sans-serif;
--font-tgl18-mono: 'JetBrains Mono', ui-monospace, Menlo, monospace;
}
.tgl-18 {
width: 100%;
min-height: 100svh;
}
.tgl-18 ::selection {
background: var(--color-tgl18-acc);
color: #fff;
}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-tgl18-bg: #14101a;
--color-tgl18-bg: oklch(0.18 0.018 310);
--color-tgl18-mut: #a396ad;
--color-tgl18-mut: oklch(0.72 0.02 315);
--color-tgl18-acc: #d06fc8;
--color-tgl18-acc: oklch(0.7 0.17 330);
--font-tgl18-sans: 'Space Grotesk', system-ui, sans-serif;
--font-tgl18-mono: 'JetBrains Mono', ui-monospace, Menlo, monospace;
}
.tgl-18 {
width: 100%;
min-height: 100svh;
}
.tgl-18 ::selection {
background: var(--color-tgl18-acc);
color: #fff;
}@import "tailwindcss";
@theme {
--color-tgl18-bg: #14101a;
--color-tgl18-bg: oklch(0.18 0.018 310);
--color-tgl18-mut: #a396ad;
--color-tgl18-mut: oklch(0.72 0.02 315);
--color-tgl18-acc: #d06fc8;
--color-tgl18-acc: oklch(0.7 0.17 330);
--font-tgl18-sans: 'Space Grotesk', system-ui, sans-serif;
--font-tgl18-mono: 'JetBrains Mono', ui-monospace, Menlo, monospace;
}
.tgl-18 {
width: 100%;
min-height: 100svh;
}
.tgl-18 ::selection {
background: var(--color-tgl18-acc);
color: #fff;
}<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=Space+Grotesk:wght@400;500;600;700&family=JetBrains+Mono:wght@400;500&display=swap">
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link rel="stylesheet" href="https://fonts.googleapis.com/css2?family=Space+Grotesk:wght@400;500;600;700&family=JetBrains+Mono:wght@400;500&display=swap">What changed in the port
Which parts became utilities, which stayed CSS, and why.
- Eight
nth-childrules become two utilities per card. The deck's order now lives in the DOM alone, which is the difference between a demo and something a CMS can render. origin-bottomis a real utility and worth stating explicitly: the fan reads as a hand of cards only because the pivot is at the base.- UI change from the CSS original: the hint line went from
#8a7e96to a lighter token — at 11px uppercase with 0.16em tracking the original sat at 3.4:1 on the dark stage, below the small-text floor.
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 3D Perspective Depth Stack Gallery That Fans on Focus
Source: https://codefronts.com/snippets/tailwind-css-image-gallery/tailwind-3d-perspective-depth-stack-gallery/
A deck of photographs stacked in 3D that fans into a cascade when you hover or focus it. Each card's resting depth and fanned position are two utilities on the card itself, which replaces four nth-child rules with markup you can reorder freely.
## HTML
```html
<section class="tgl-18 w-full min-h-svh flex flex-col items-center justify-center gap-7 bg-tgl18-bg font-tgl18-sans px-5 py-10" aria-label="Perspective depth stack gallery">
<div class="group relative w-[260px] h-[330px] [perspective:1200px]">
<a href="#" class="absolute inset-0 rounded-2xl overflow-hidden transform-3d origin-bottom shadow-[0_20px_40px_-20px_rgba(0,0,0,0.7)] [transform:translateZ(60px)] transition-transform duration-[550ms] ease-[cubic-bezier(0.2,0.7,0.2,1)] group-hover:[transform:translateX(-150px)_rotate(-9deg)] group-focus-within:[transform:translateX(-150px)_rotate(-9deg)] focus-visible:outline-3 focus-visible:outline-tgl18-acc focus-visible:outline-offset-[3px] focus-visible:z-[5] motion-reduce:transition-none">
<img src="https://images.unsplash.com/photo-1607746882042-944635dfe10e?q=80&w=500&h=620&auto=format&fit=crop" alt="Studio portrait in high contrast" loading="lazy" decoding="async" class="block w-full h-full object-cover">
</a>
<a href="#" class="absolute inset-0 rounded-2xl overflow-hidden transform-3d origin-bottom shadow-[0_20px_40px_-20px_rgba(0,0,0,0.7)] [transform:translateZ(40px)_translateY(10px)_scale(0.97)] transition-transform duration-[550ms] ease-[cubic-bezier(0.2,0.7,0.2,1)] group-hover:[transform:translateX(-50px)_rotate(-3deg)] group-focus-within:[transform:translateX(-50px)_rotate(-3deg)] focus-visible:outline-3 focus-visible:outline-tgl18-acc focus-visible:outline-offset-[3px] focus-visible:z-[5] motion-reduce:transition-none">
<img src="https://images.unsplash.com/photo-1505691938895-1758d7feb511?q=80&w=500&h=620&auto=format&fit=crop" alt="Minimal landscape study" loading="lazy" decoding="async" class="block w-full h-full object-cover">
</a>
<a href="#" class="absolute inset-0 rounded-2xl overflow-hidden transform-3d origin-bottom shadow-[0_20px_40px_-20px_rgba(0,0,0,0.7)] [transform:translateZ(20px)_translateY(20px)_scale(0.94)] transition-transform duration-[550ms] ease-[cubic-bezier(0.2,0.7,0.2,1)] group-hover:[transform:translateX(50px)_rotate(3deg)] group-focus-within:[transform:translateX(50px)_rotate(3deg)] focus-visible:outline-3 focus-visible:outline-tgl18-acc focus-visible:outline-offset-[3px] focus-visible:z-[5] motion-reduce:transition-none">
<img src="https://images.unsplash.com/photo-1470252649378-9c29740c9fa8?q=80&w=500&h=620&auto=format&fit=crop" alt="Street scene in late light" loading="lazy" decoding="async" class="block w-full h-full object-cover">
</a>
<a href="#" class="absolute inset-0 rounded-2xl overflow-hidden transform-3d origin-bottom shadow-[0_20px_40px_-20px_rgba(0,0,0,0.7)] [transform:translateZ(0)_translateY(30px)_scale(0.91)] transition-transform duration-[550ms] ease-[cubic-bezier(0.2,0.7,0.2,1)] group-hover:[transform:translateX(150px)_rotate(9deg)] group-focus-within:[transform:translateX(150px)_rotate(9deg)] focus-visible:outline-3 focus-visible:outline-tgl18-acc focus-visible:outline-offset-[3px] focus-visible:z-[5] motion-reduce:transition-none">
<img src="https://images.unsplash.com/photo-1450101499163-c8848c66ca85?q=80&w=500&h=620&auto=format&fit=crop" alt="Botanical detail on black" loading="lazy" decoding="async" class="block w-full h-full object-cover">
</a>
</div>
<p class="font-tgl18-mono text-[11px] tracking-[0.16em] uppercase text-tgl18-mut m-0">hover or focus the stack to fan it out</p>
</section>
```
## CSS
```css
@import "tailwindcss";
@theme {
--color-tgl18-bg: #14101a;
--color-tgl18-bg: oklch(0.18 0.018 310);
--color-tgl18-mut: #a396ad;
--color-tgl18-mut: oklch(0.72 0.02 315);
--color-tgl18-acc: #d06fc8;
--color-tgl18-acc: oklch(0.7 0.17 330);
--font-tgl18-sans: 'Space Grotesk', system-ui, sans-serif;
--font-tgl18-mono: 'JetBrains Mono', ui-monospace, Menlo, monospace;
}
.tgl-18 {
width: 100%;
min-height: 100svh;
}
.tgl-18 ::selection {
background: var(--color-tgl18-acc);
color: #fff;
}
```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 3D Perspective Depth Stack Gallery That Fans on Focus
Source: https://codefronts.com/snippets/tailwind-css-image-gallery/tailwind-3d-perspective-depth-stack-gallery/
A deck of photographs stacked in 3D that fans into a cascade when you hover or focus it. Each card's resting depth and fanned position are two utilities on the card itself, which replaces four nth-child rules with markup you can reorder freely.
## HTML
```html
<section class="tgl-18 w-full min-h-svh flex flex-col items-center justify-center gap-7 bg-tgl18-bg font-tgl18-sans px-5 py-10" aria-label="Perspective depth stack gallery">
<div class="group relative w-[260px] h-[330px] [perspective:1200px]">
<a href="#" class="absolute inset-0 rounded-2xl overflow-hidden transform-3d origin-bottom shadow-[0_20px_40px_-20px_rgba(0,0,0,0.7)] [transform:translateZ(60px)] transition-transform duration-[550ms] ease-[cubic-bezier(0.2,0.7,0.2,1)] group-hover:[transform:translateX(-150px)_rotate(-9deg)] group-focus-within:[transform:translateX(-150px)_rotate(-9deg)] focus-visible:outline-3 focus-visible:outline-tgl18-acc focus-visible:outline-offset-[3px] focus-visible:z-[5] motion-reduce:transition-none">
<img src="https://images.unsplash.com/photo-1607746882042-944635dfe10e?q=80&w=500&h=620&auto=format&fit=crop" alt="Studio portrait in high contrast" loading="lazy" decoding="async" class="block w-full h-full object-cover">
</a>
<a href="#" class="absolute inset-0 rounded-2xl overflow-hidden transform-3d origin-bottom shadow-[0_20px_40px_-20px_rgba(0,0,0,0.7)] [transform:translateZ(40px)_translateY(10px)_scale(0.97)] transition-transform duration-[550ms] ease-[cubic-bezier(0.2,0.7,0.2,1)] group-hover:[transform:translateX(-50px)_rotate(-3deg)] group-focus-within:[transform:translateX(-50px)_rotate(-3deg)] focus-visible:outline-3 focus-visible:outline-tgl18-acc focus-visible:outline-offset-[3px] focus-visible:z-[5] motion-reduce:transition-none">
<img src="https://images.unsplash.com/photo-1505691938895-1758d7feb511?q=80&w=500&h=620&auto=format&fit=crop" alt="Minimal landscape study" loading="lazy" decoding="async" class="block w-full h-full object-cover">
</a>
<a href="#" class="absolute inset-0 rounded-2xl overflow-hidden transform-3d origin-bottom shadow-[0_20px_40px_-20px_rgba(0,0,0,0.7)] [transform:translateZ(20px)_translateY(20px)_scale(0.94)] transition-transform duration-[550ms] ease-[cubic-bezier(0.2,0.7,0.2,1)] group-hover:[transform:translateX(50px)_rotate(3deg)] group-focus-within:[transform:translateX(50px)_rotate(3deg)] focus-visible:outline-3 focus-visible:outline-tgl18-acc focus-visible:outline-offset-[3px] focus-visible:z-[5] motion-reduce:transition-none">
<img src="https://images.unsplash.com/photo-1470252649378-9c29740c9fa8?q=80&w=500&h=620&auto=format&fit=crop" alt="Street scene in late light" loading="lazy" decoding="async" class="block w-full h-full object-cover">
</a>
<a href="#" class="absolute inset-0 rounded-2xl overflow-hidden transform-3d origin-bottom shadow-[0_20px_40px_-20px_rgba(0,0,0,0.7)] [transform:translateZ(0)_translateY(30px)_scale(0.91)] transition-transform duration-[550ms] ease-[cubic-bezier(0.2,0.7,0.2,1)] group-hover:[transform:translateX(150px)_rotate(9deg)] group-focus-within:[transform:translateX(150px)_rotate(9deg)] focus-visible:outline-3 focus-visible:outline-tgl18-acc focus-visible:outline-offset-[3px] focus-visible:z-[5] motion-reduce:transition-none">
<img src="https://images.unsplash.com/photo-1450101499163-c8848c66ca85?q=80&w=500&h=620&auto=format&fit=crop" alt="Botanical detail on black" loading="lazy" decoding="async" class="block w-full h-full object-cover">
</a>
</div>
<p class="font-tgl18-mono text-[11px] tracking-[0.16em] uppercase text-tgl18-mut m-0">hover or focus the stack to fan it out</p>
</section>
```
## CSS
```css
@import "tailwindcss";
@theme {
--color-tgl18-bg: #14101a;
--color-tgl18-bg: oklch(0.18 0.018 310);
--color-tgl18-mut: #a396ad;
--color-tgl18-mut: oklch(0.72 0.02 315);
--color-tgl18-acc: #d06fc8;
--color-tgl18-acc: oklch(0.7 0.17 330);
--font-tgl18-sans: 'Space Grotesk', system-ui, sans-serif;
--font-tgl18-mono: 'JetBrains Mono', ui-monospace, Menlo, monospace;
}
.tgl-18 {
width: 100%;
min-height: 100svh;
}
.tgl-18 ::selection {
background: var(--color-tgl18-acc);
color: #fff;
}
```How this works
Per-card transforms beat nth-child. The CSS version encoded card order in eight selectors; here each card owns both of its poses. Reordering the deck is moving an element, not renumbering a stylesheet.
Two states, one group. The stack is the group; hover or focus anywhere inside it fans every card at once, which is what makes the deck feel like one object rather than four.
Keyboard gets the same deck. Every card is a focusable anchor, so tabbing into the stack fans it and the card behind the top one is reachable — the original hid three of four photographs from keyboard users until focus happened to land right.
Make it yours
- Resting depth: each card's
translateZ/scalestep. - Fan spread: the
translateXandrotatevalues in the hover pose. - Add a fifth card by copying one and giving it the next depth step and fan position.
Gotchas — read before shipping
- Give the stack a fixed size. Absolutely-positioned cards need a frame, or the fan has nothing to be measured against.
- Keep the
translateZsteps small relative to the perspective value, or back cards clip through the perspective origin. group-focus-within:is what makes the deck keyboard-usable — without it three of four photographs are unreachable in practice.
Browser support
| Chrome | Safari | Firefox | Edge |
|---|---|---|---|
| 111+ | 16.4+ | 128+ | 111+ |
3D transforms and :focus-within are universal; the floor is Tailwind v4's generated sheet.