20 / 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 Ken Burns Slideshow Gallery with Staggered Cross-Fade
A cinematic auto-playing slideshow where each image slowly zooms and pans while cross-fading to the next. Two animations per slide — one for the fade, one for the pan — both minted as @theme utilities, staggered by four [animation-delay:...] values.
Untitled UI Pro: 5,000+ React + Tailwind CSS v4 components · from $349 one-time
Untitled UI React · Affiliate link
The code
<section class="tgl-20 w-full min-h-svh grid place-items-center bg-tgl20-bg font-tgl20-sans px-5 py-10" aria-label="Ken Burns slideshow" aria-roledescription="carousel">
<div class="relative w-[min(920px,100%)] [aspect-ratio:12/7] rounded-2xl overflow-hidden shadow-[0_40px_80px_-40px_rgba(0,0,0,0.9)]">
<figure class="absolute inset-0 m-0 opacity-0 animate-tgl20-fade [animation-delay:0s] motion-reduce:animate-none motion-reduce:opacity-100">
<img src="https://images.unsplash.com/photo-1418985991508-e47386d96a71?q=80&w=1200&h=700&auto=format&fit=crop" alt="Sweeping mountain vista" loading="lazy" decoding="async" class="w-full h-full object-cover origin-center animate-tgl20-kb [animation-delay:0s] motion-reduce:animate-none motion-reduce:scale-105">
<figcaption class="absolute inset-x-0 bottom-0 px-8 py-7 w-full text-white font-tgl20-serif text-[26px] font-bold tracking-[-0.01em] bg-[linear-gradient(0deg,rgba(0,0,0,0.65),transparent)]">The High Passes</figcaption>
</figure>
<figure class="absolute inset-0 m-0 opacity-0 animate-tgl20-fade [animation-delay:6s] motion-reduce:animate-none">
<img src="https://images.unsplash.com/photo-1517701550927-30cf4ba1dba5?q=80&w=1200&h=700&auto=format&fit=crop" alt="Open ocean at golden hour" loading="lazy" decoding="async" class="w-full h-full object-cover origin-center animate-tgl20-kb [animation-delay:6s] motion-reduce:animate-none">
<figcaption class="absolute inset-x-0 bottom-0 px-8 py-7 w-full text-white font-tgl20-serif text-[26px] font-bold tracking-[-0.01em] bg-[linear-gradient(0deg,rgba(0,0,0,0.65),transparent)]">Endless Blue</figcaption>
</figure>
<figure class="absolute inset-0 m-0 opacity-0 animate-tgl20-fade [animation-delay:12s] motion-reduce:animate-none">
<img src="https://images.unsplash.com/photo-1517180102446-f3ece451e9d8?q=80&w=1200&h=700&auto=format&fit=crop" alt="Desert at dusk" loading="lazy" decoding="async" class="w-full h-full object-cover origin-center animate-tgl20-kb [animation-delay:12s] motion-reduce:animate-none">
<figcaption class="absolute inset-x-0 bottom-0 px-8 py-7 w-full text-white font-tgl20-serif text-[26px] font-bold tracking-[-0.01em] bg-[linear-gradient(0deg,rgba(0,0,0,0.65),transparent)]">The Dry Lands</figcaption>
</figure>
<figure class="absolute inset-0 m-0 opacity-0 animate-tgl20-fade [animation-delay:18s] motion-reduce:animate-none">
<img src="https://images.unsplash.com/photo-1600607687939-ce8a6c25118c?q=80&w=1200&h=700&auto=format&fit=crop" alt="Ancient forest interior" loading="lazy" decoding="async" class="w-full h-full object-cover origin-center animate-tgl20-kb [animation-delay:18s] motion-reduce:animate-none">
<figcaption class="absolute inset-x-0 bottom-0 px-8 py-7 w-full text-white font-tgl20-serif text-[26px] font-bold tracking-[-0.01em] bg-[linear-gradient(0deg,rgba(0,0,0,0.65),transparent)]">Old Growth</figcaption>
</figure>
</div>
</section><section class="tgl-20 w-full min-h-svh grid place-items-center bg-tgl20-bg font-tgl20-sans px-5 py-10" aria-label="Ken Burns slideshow" aria-roledescription="carousel">
<div class="relative w-[min(920px,100%)] [aspect-ratio:12/7] rounded-2xl overflow-hidden shadow-[0_40px_80px_-40px_rgba(0,0,0,0.9)]">
<figure class="absolute inset-0 m-0 opacity-0 animate-tgl20-fade [animation-delay:0s] motion-reduce:animate-none motion-reduce:opacity-100">
<img src="https://images.unsplash.com/photo-1418985991508-e47386d96a71?q=80&w=1200&h=700&auto=format&fit=crop" alt="Sweeping mountain vista" loading="lazy" decoding="async" class="w-full h-full object-cover origin-center animate-tgl20-kb [animation-delay:0s] motion-reduce:animate-none motion-reduce:scale-105">
<figcaption class="absolute inset-x-0 bottom-0 px-8 py-7 w-full text-white font-tgl20-serif text-[26px] font-bold tracking-[-0.01em] bg-[linear-gradient(0deg,rgba(0,0,0,0.65),transparent)]">The High Passes</figcaption>
</figure>
<figure class="absolute inset-0 m-0 opacity-0 animate-tgl20-fade [animation-delay:6s] motion-reduce:animate-none">
<img src="https://images.unsplash.com/photo-1517701550927-30cf4ba1dba5?q=80&w=1200&h=700&auto=format&fit=crop" alt="Open ocean at golden hour" loading="lazy" decoding="async" class="w-full h-full object-cover origin-center animate-tgl20-kb [animation-delay:6s] motion-reduce:animate-none">
<figcaption class="absolute inset-x-0 bottom-0 px-8 py-7 w-full text-white font-tgl20-serif text-[26px] font-bold tracking-[-0.01em] bg-[linear-gradient(0deg,rgba(0,0,0,0.65),transparent)]">Endless Blue</figcaption>
</figure>
<figure class="absolute inset-0 m-0 opacity-0 animate-tgl20-fade [animation-delay:12s] motion-reduce:animate-none">
<img src="https://images.unsplash.com/photo-1517180102446-f3ece451e9d8?q=80&w=1200&h=700&auto=format&fit=crop" alt="Desert at dusk" loading="lazy" decoding="async" class="w-full h-full object-cover origin-center animate-tgl20-kb [animation-delay:12s] motion-reduce:animate-none">
<figcaption class="absolute inset-x-0 bottom-0 px-8 py-7 w-full text-white font-tgl20-serif text-[26px] font-bold tracking-[-0.01em] bg-[linear-gradient(0deg,rgba(0,0,0,0.65),transparent)]">The Dry Lands</figcaption>
</figure>
<figure class="absolute inset-0 m-0 opacity-0 animate-tgl20-fade [animation-delay:18s] motion-reduce:animate-none">
<img src="https://images.unsplash.com/photo-1600607687939-ce8a6c25118c?q=80&w=1200&h=700&auto=format&fit=crop" alt="Ancient forest interior" loading="lazy" decoding="async" class="w-full h-full object-cover origin-center animate-tgl20-kb [animation-delay:18s] motion-reduce:animate-none">
<figcaption class="absolute inset-x-0 bottom-0 px-8 py-7 w-full text-white font-tgl20-serif text-[26px] font-bold tracking-[-0.01em] bg-[linear-gradient(0deg,rgba(0,0,0,0.65),transparent)]">Old Growth</figcaption>
</figure>
</div>
</section>@import "tailwindcss";
@theme {
--color-tgl20-bg: #0a0a0c;
--color-tgl20-bg: oklch(0.13 0.004 280);
--color-tgl20-acc: #d9a441;
--color-tgl20-acc: oklch(0.76 0.12 75);
--font-tgl20-sans: 'Space Grotesk', system-ui, sans-serif;
--font-tgl20-serif: 'Playfair Display', Georgia, serif;
--animate-tgl20-fade: tgl20-fade 24s infinite;
--animate-tgl20-kb: tgl20-kb 24s infinite;
}
@keyframes tgl20-fade {
0% {
opacity: 0;
}
2% {
opacity: 1;
}
23% {
opacity: 1;
}
27% {
opacity: 0;
}
100% {
opacity: 0;
}
}
@keyframes tgl20-kb {
0% {
transform: scale(1) translate(0, 0);
}
27% {
transform: scale(1.18) translate(-3%, 2%);
}
100% {
transform: scale(1.18) translate(-3%, 2%);
}
}
.tgl-20 {
width: 100%;
min-height: 100svh;
}
.tgl-20 ::selection {
background: var(--color-tgl20-acc);
color: #111;
}@import "tailwindcss";
@theme {
--color-tgl20-bg: #0a0a0c;
--color-tgl20-bg: oklch(0.13 0.004 280);
--color-tgl20-acc: #d9a441;
--color-tgl20-acc: oklch(0.76 0.12 75);
--font-tgl20-sans: 'Space Grotesk', system-ui, sans-serif;
--font-tgl20-serif: 'Playfair Display', Georgia, serif;
--animate-tgl20-fade: tgl20-fade 24s infinite;
--animate-tgl20-kb: tgl20-kb 24s infinite;
}
@keyframes tgl20-fade {
0% {
opacity: 0;
}
2% {
opacity: 1;
}
23% {
opacity: 1;
}
27% {
opacity: 0;
}
100% {
opacity: 0;
}
}
@keyframes tgl20-kb {
0% {
transform: scale(1) translate(0, 0);
}
27% {
transform: scale(1.18) translate(-3%, 2%);
}
100% {
transform: scale(1.18) translate(-3%, 2%);
}
}
.tgl-20 {
width: 100%;
min-height: 100svh;
}
.tgl-20 ::selection {
background: var(--color-tgl20-acc);
color: #111;
}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-tgl20-bg: #0a0a0c;
--color-tgl20-bg: oklch(0.13 0.004 280);
--color-tgl20-acc: #d9a441;
--color-tgl20-acc: oklch(0.76 0.12 75);
--font-tgl20-sans: 'Space Grotesk', system-ui, sans-serif;
--font-tgl20-serif: 'Playfair Display', Georgia, serif;
--animate-tgl20-fade: tgl20-fade 24s infinite;
--animate-tgl20-kb: tgl20-kb 24s infinite;
}
@keyframes tgl20-fade {
0% {
opacity: 0;
}
2% {
opacity: 1;
}
23% {
opacity: 1;
}
27% {
opacity: 0;
}
100% {
opacity: 0;
}
}
@keyframes tgl20-kb {
0% {
transform: scale(1) translate(0, 0);
}
27% {
transform: scale(1.18) translate(-3%, 2%);
}
100% {
transform: scale(1.18) translate(-3%, 2%);
}
}
.tgl-20 {
width: 100%;
min-height: 100svh;
}
.tgl-20 ::selection {
background: var(--color-tgl20-acc);
color: #111;
}@import "tailwindcss";
@theme {
--color-tgl20-bg: #0a0a0c;
--color-tgl20-bg: oklch(0.13 0.004 280);
--color-tgl20-acc: #d9a441;
--color-tgl20-acc: oklch(0.76 0.12 75);
--font-tgl20-sans: 'Space Grotesk', system-ui, sans-serif;
--font-tgl20-serif: 'Playfair Display', Georgia, serif;
--animate-tgl20-fade: tgl20-fade 24s infinite;
--animate-tgl20-kb: tgl20-kb 24s infinite;
}
@keyframes tgl20-fade {
0% {
opacity: 0;
}
2% {
opacity: 1;
}
23% {
opacity: 1;
}
27% {
opacity: 0;
}
100% {
opacity: 0;
}
}
@keyframes tgl20-kb {
0% {
transform: scale(1) translate(0, 0);
}
27% {
transform: scale(1.18) translate(-3%, 2%);
}
100% {
transform: scale(1.18) translate(-3%, 2%);
}
}
.tgl-20 {
width: 100%;
min-height: 100svh;
}
.tgl-20 ::selection {
background: var(--color-tgl20-acc);
color: #111;
}<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=Playfair+Display:wght@700&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=Playfair+Display:wght@700&display=swap">What changed in the port
Which parts became utilities, which stayed CSS, and why.
- Two
@themeanimations rather than one: the fade belongs to the slide and the pan to the image, and keeping them separate is what lets you slow the pan without resequencing the slideshow. - Delays stay arbitrary values.
delay-*in Tailwind is a transition delay utility; animation delay has no scale, which is correct — 6s here is sequencing, not design. - UI change from the CSS original: the caption face is a real display serif loaded in
setuprather than Georgia, and the scrim went to 0.65 alpha so 26px white type clears its background on the brightest slide.
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 Ken Burns Slideshow Gallery with Staggered Cross-Fade
Source: https://codefronts.com/snippets/tailwind-css-image-gallery/tailwind-ken-burns-slideshow-gallery-cross-fade/
A cinematic auto-playing slideshow where each image slowly zooms and pans while cross-fading to the next. Two animations per slide — one for the fade, one for the pan — both minted as @theme utilities, staggered by four [animation-delay:...] values.
## HTML
```html
<section class="tgl-20 w-full min-h-svh grid place-items-center bg-tgl20-bg font-tgl20-sans px-5 py-10" aria-label="Ken Burns slideshow" aria-roledescription="carousel">
<div class="relative w-[min(920px,100%)] [aspect-ratio:12/7] rounded-2xl overflow-hidden shadow-[0_40px_80px_-40px_rgba(0,0,0,0.9)]">
<figure class="absolute inset-0 m-0 opacity-0 animate-tgl20-fade [animation-delay:0s] motion-reduce:animate-none motion-reduce:opacity-100">
<img src="https://images.unsplash.com/photo-1418985991508-e47386d96a71?q=80&w=1200&h=700&auto=format&fit=crop" alt="Sweeping mountain vista" loading="lazy" decoding="async" class="w-full h-full object-cover origin-center animate-tgl20-kb [animation-delay:0s] motion-reduce:animate-none motion-reduce:scale-105">
<figcaption class="absolute inset-x-0 bottom-0 px-8 py-7 w-full text-white font-tgl20-serif text-[26px] font-bold tracking-[-0.01em] bg-[linear-gradient(0deg,rgba(0,0,0,0.65),transparent)]">The High Passes</figcaption>
</figure>
<figure class="absolute inset-0 m-0 opacity-0 animate-tgl20-fade [animation-delay:6s] motion-reduce:animate-none">
<img src="https://images.unsplash.com/photo-1517701550927-30cf4ba1dba5?q=80&w=1200&h=700&auto=format&fit=crop" alt="Open ocean at golden hour" loading="lazy" decoding="async" class="w-full h-full object-cover origin-center animate-tgl20-kb [animation-delay:6s] motion-reduce:animate-none">
<figcaption class="absolute inset-x-0 bottom-0 px-8 py-7 w-full text-white font-tgl20-serif text-[26px] font-bold tracking-[-0.01em] bg-[linear-gradient(0deg,rgba(0,0,0,0.65),transparent)]">Endless Blue</figcaption>
</figure>
<figure class="absolute inset-0 m-0 opacity-0 animate-tgl20-fade [animation-delay:12s] motion-reduce:animate-none">
<img src="https://images.unsplash.com/photo-1517180102446-f3ece451e9d8?q=80&w=1200&h=700&auto=format&fit=crop" alt="Desert at dusk" loading="lazy" decoding="async" class="w-full h-full object-cover origin-center animate-tgl20-kb [animation-delay:12s] motion-reduce:animate-none">
<figcaption class="absolute inset-x-0 bottom-0 px-8 py-7 w-full text-white font-tgl20-serif text-[26px] font-bold tracking-[-0.01em] bg-[linear-gradient(0deg,rgba(0,0,0,0.65),transparent)]">The Dry Lands</figcaption>
</figure>
<figure class="absolute inset-0 m-0 opacity-0 animate-tgl20-fade [animation-delay:18s] motion-reduce:animate-none">
<img src="https://images.unsplash.com/photo-1600607687939-ce8a6c25118c?q=80&w=1200&h=700&auto=format&fit=crop" alt="Ancient forest interior" loading="lazy" decoding="async" class="w-full h-full object-cover origin-center animate-tgl20-kb [animation-delay:18s] motion-reduce:animate-none">
<figcaption class="absolute inset-x-0 bottom-0 px-8 py-7 w-full text-white font-tgl20-serif text-[26px] font-bold tracking-[-0.01em] bg-[linear-gradient(0deg,rgba(0,0,0,0.65),transparent)]">Old Growth</figcaption>
</figure>
</div>
</section>
```
## CSS
```css
@import "tailwindcss";
@theme {
--color-tgl20-bg: #0a0a0c;
--color-tgl20-bg: oklch(0.13 0.004 280);
--color-tgl20-acc: #d9a441;
--color-tgl20-acc: oklch(0.76 0.12 75);
--font-tgl20-sans: 'Space Grotesk', system-ui, sans-serif;
--font-tgl20-serif: 'Playfair Display', Georgia, serif;
--animate-tgl20-fade: tgl20-fade 24s infinite;
--animate-tgl20-kb: tgl20-kb 24s infinite;
}
@keyframes tgl20-fade {
0% {
opacity: 0;
}
2% {
opacity: 1;
}
23% {
opacity: 1;
}
27% {
opacity: 0;
}
100% {
opacity: 0;
}
}
@keyframes tgl20-kb {
0% {
transform: scale(1) translate(0, 0);
}
27% {
transform: scale(1.18) translate(-3%, 2%);
}
100% {
transform: scale(1.18) translate(-3%, 2%);
}
}
.tgl-20 {
width: 100%;
min-height: 100svh;
}
.tgl-20 ::selection {
background: var(--color-tgl20-acc);
color: #111;
}
```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 Ken Burns Slideshow Gallery with Staggered Cross-Fade
Source: https://codefronts.com/snippets/tailwind-css-image-gallery/tailwind-ken-burns-slideshow-gallery-cross-fade/
A cinematic auto-playing slideshow where each image slowly zooms and pans while cross-fading to the next. Two animations per slide — one for the fade, one for the pan — both minted as @theme utilities, staggered by four [animation-delay:...] values.
## HTML
```html
<section class="tgl-20 w-full min-h-svh grid place-items-center bg-tgl20-bg font-tgl20-sans px-5 py-10" aria-label="Ken Burns slideshow" aria-roledescription="carousel">
<div class="relative w-[min(920px,100%)] [aspect-ratio:12/7] rounded-2xl overflow-hidden shadow-[0_40px_80px_-40px_rgba(0,0,0,0.9)]">
<figure class="absolute inset-0 m-0 opacity-0 animate-tgl20-fade [animation-delay:0s] motion-reduce:animate-none motion-reduce:opacity-100">
<img src="https://images.unsplash.com/photo-1418985991508-e47386d96a71?q=80&w=1200&h=700&auto=format&fit=crop" alt="Sweeping mountain vista" loading="lazy" decoding="async" class="w-full h-full object-cover origin-center animate-tgl20-kb [animation-delay:0s] motion-reduce:animate-none motion-reduce:scale-105">
<figcaption class="absolute inset-x-0 bottom-0 px-8 py-7 w-full text-white font-tgl20-serif text-[26px] font-bold tracking-[-0.01em] bg-[linear-gradient(0deg,rgba(0,0,0,0.65),transparent)]">The High Passes</figcaption>
</figure>
<figure class="absolute inset-0 m-0 opacity-0 animate-tgl20-fade [animation-delay:6s] motion-reduce:animate-none">
<img src="https://images.unsplash.com/photo-1517701550927-30cf4ba1dba5?q=80&w=1200&h=700&auto=format&fit=crop" alt="Open ocean at golden hour" loading="lazy" decoding="async" class="w-full h-full object-cover origin-center animate-tgl20-kb [animation-delay:6s] motion-reduce:animate-none">
<figcaption class="absolute inset-x-0 bottom-0 px-8 py-7 w-full text-white font-tgl20-serif text-[26px] font-bold tracking-[-0.01em] bg-[linear-gradient(0deg,rgba(0,0,0,0.65),transparent)]">Endless Blue</figcaption>
</figure>
<figure class="absolute inset-0 m-0 opacity-0 animate-tgl20-fade [animation-delay:12s] motion-reduce:animate-none">
<img src="https://images.unsplash.com/photo-1517180102446-f3ece451e9d8?q=80&w=1200&h=700&auto=format&fit=crop" alt="Desert at dusk" loading="lazy" decoding="async" class="w-full h-full object-cover origin-center animate-tgl20-kb [animation-delay:12s] motion-reduce:animate-none">
<figcaption class="absolute inset-x-0 bottom-0 px-8 py-7 w-full text-white font-tgl20-serif text-[26px] font-bold tracking-[-0.01em] bg-[linear-gradient(0deg,rgba(0,0,0,0.65),transparent)]">The Dry Lands</figcaption>
</figure>
<figure class="absolute inset-0 m-0 opacity-0 animate-tgl20-fade [animation-delay:18s] motion-reduce:animate-none">
<img src="https://images.unsplash.com/photo-1600607687939-ce8a6c25118c?q=80&w=1200&h=700&auto=format&fit=crop" alt="Ancient forest interior" loading="lazy" decoding="async" class="w-full h-full object-cover origin-center animate-tgl20-kb [animation-delay:18s] motion-reduce:animate-none">
<figcaption class="absolute inset-x-0 bottom-0 px-8 py-7 w-full text-white font-tgl20-serif text-[26px] font-bold tracking-[-0.01em] bg-[linear-gradient(0deg,rgba(0,0,0,0.65),transparent)]">Old Growth</figcaption>
</figure>
</div>
</section>
```
## CSS
```css
@import "tailwindcss";
@theme {
--color-tgl20-bg: #0a0a0c;
--color-tgl20-bg: oklch(0.13 0.004 280);
--color-tgl20-acc: #d9a441;
--color-tgl20-acc: oklch(0.76 0.12 75);
--font-tgl20-sans: 'Space Grotesk', system-ui, sans-serif;
--font-tgl20-serif: 'Playfair Display', Georgia, serif;
--animate-tgl20-fade: tgl20-fade 24s infinite;
--animate-tgl20-kb: tgl20-kb 24s infinite;
}
@keyframes tgl20-fade {
0% {
opacity: 0;
}
2% {
opacity: 1;
}
23% {
opacity: 1;
}
27% {
opacity: 0;
}
100% {
opacity: 0;
}
}
@keyframes tgl20-kb {
0% {
transform: scale(1) translate(0, 0);
}
27% {
transform: scale(1.18) translate(-3%, 2%);
}
100% {
transform: scale(1.18) translate(-3%, 2%);
}
}
.tgl-20 {
width: 100%;
min-height: 100svh;
}
.tgl-20 ::selection {
background: var(--color-tgl20-acc);
color: #111;
}
```How this works
Every slide runs the same two animations; only the delay differs. A 24-second cycle divided by four slides is a six-second stagger, and each slide's [animation-delay] states its place in the sequence. The fade keyframe holds each slide visible for its slot and transparent for the rest.
The pan is the second animation. The image scales from 1 to 1.18 and drifts a few percent, which is what makes a still photograph feel filmed.
The static fallback is explicit. Under motion-reduce: both animations stop and the first slide is forced visible with a small resting zoom — otherwise stopping the animation would leave every slide at opacity-0 and the hero blank.
Make it yours
- Slide duration: total ÷ slide count, then update the four delays together.
- Drama: the scale and translate range in the Ken Burns keyframe.
- Alternate direction per slide (zoom in on odd, out on even) for variety across a long loop.
Gotchas — read before shipping
- Delays must be consistent on the slide AND its image, or the pan drifts out of step with the fade.
- Do not forget
motion-reduce:opacity-100on the first slide. Stopping the animation alone leaves every slide hidden. - Keep the fade windows overlapping slightly; a gap between slides shows the frame's background for a frame or two.
Browser support
| Chrome | Safari | Firefox | Edge |
|---|---|---|---|
| 111+ | 16.4+ | 128+ | 111+ |
Keyframe opacity and transform animation is universal; the floor is Tailwind v4's generated sheet.