22 CSS Image Galleries20 / 22

Pure CSSMIT licensed

CSS Ken Burns Slideshow Gallery

A cinematic auto-playing slideshow where each image slowly zooms and pans (the 'Ken Burns' effect) while cross-fading to the next — the high-production hero slideshow documentary sites and landing pages search for. Pure CSS keyframes.

Published Updated

Live Demo
Try it

The code

<section class="gl-19" aria-label="Ken Burns slideshow" aria-roledescription="carousel">
  <div class="gl-19__frame">
    <figure class="gl-19__slide gl-19__slide--1"><img src="https://loremflickr.com/1200/700/mountain,cinematic?lock=1901" alt="Sweeping mountain vista" loading="lazy" decoding="async"><figcaption>The High Passes</figcaption></figure>
    <figure class="gl-19__slide gl-19__slide--2"><img src="https://loremflickr.com/1200/700/ocean,cinematic?lock=1902" alt="Open ocean at golden hour" loading="lazy" decoding="async"><figcaption>Endless Blue</figcaption></figure>
    <figure class="gl-19__slide gl-19__slide--3"><img src="https://loremflickr.com/1200/700/desert,cinematic?lock=1903" alt="Desert at dusk" loading="lazy" decoding="async"><figcaption>The Dry Lands</figcaption></figure>
    <figure class="gl-19__slide gl-19__slide--4"><img src="https://loremflickr.com/1200/700/forest,cinematic?lock=1904" alt="Ancient forest interior" loading="lazy" decoding="async"><figcaption>Old Growth</figcaption></figure>
  </div>
</section>
.gl-19,
.gl-19 *,
.gl-19 *::before,
.gl-19 *::after {
  box-sizing: border-box;
  margin: 0;
  padding: 0;
}

.gl-19 ::selection {
  background: oklch(0.7 0.13 60);
  color: #111;
}

.gl-19 {
  font-family: 'Segoe UI',system-ui,sans-serif;
  width: 100%;
  min-height: 100vh;
  padding: 40px 20px;
  background: #0a0a0c;
  display: flex;
  align-items: center;
  justify-content: center;
}

.gl-19__frame {
  position: relative;
  width: min(920px,100%);
  aspect-ratio: 12/7;
  border-radius: 16px;
  overflow: hidden;
  box-shadow: 0 40px 80px -40px rgba(0,0,0,.9);
}

.gl-19__slide {
  position: absolute;
  inset: 0;
  opacity: 0;
  animation: gl-19-fade 24s infinite;
}

.gl-19__slide img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  transform-origin: center;
  animation: gl-19-kb 24s infinite;
}

.gl-19__slide figcaption {
  position: absolute;
  left: 0;
  bottom: 0;
  padding: 28px 30px;
  color: #fff;
  font-family: Georgia,serif;
  font-size: 26px;
  font-weight: 700;
  letter-spacing: -.01em;
  background: linear-gradient(0deg,rgba(0,0,0,.6),transparent);
  width: 100%;
}

.gl-19__slide--1 {
  animation-delay: 0s;
}

.gl-19__slide--1 img {
  animation-delay: 0s;
}

.gl-19__slide--2 {
  animation-delay: 6s;
}

.gl-19__slide--2 img {
  animation-delay: 6s;
}

.gl-19__slide--3 {
  animation-delay: 12s;
}

.gl-19__slide--3 img {
  animation-delay: 12s;
}

.gl-19__slide--4 {
  animation-delay: 18s;
}

.gl-19__slide--4 img {
  animation-delay: 18s;
}

@keyframes gl-19-fade {
  0% {
    opacity: 0;
  }

  2% {
    opacity: 1;
  }

  23% {
    opacity: 1;
  }

  27% {
    opacity: 0;
  }

  100% {
    opacity: 0;
  }
}

@keyframes gl-19-kb {
  0% {
    transform: scale(1) translate(0,0);
  }

  27% {
    transform: scale(1.18) translate(-3%,2%);
  }

  100% {
    transform: scale(1.18) translate(-3%,2%);
  }
}

@media (prefers-reduced-motion: reduce) {
  .gl-19__slide,
    .gl-19__slide img {
    animation: none;
  }

  .gl-19__slide--1 {
    opacity: 1;
  }

  .gl-19__slide--1 img {
    transform: scale(1.05);
  }
}
/* No JavaScript — staggered opacity cross-fade + a slow transform pan per slide. */
Paste this into ChatGPT, Claude, Cursor, or any coding assistant. The block below is pre-framed with everything the AI needs to integrate this demo into your project — markup, styles, scoping notes, and the source URL. Hit Copy and paste straight into your chat.
Here's a working CSS Image Gallerie 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: CSS Ken Burns Slideshow Gallery
Source: https://codefronts.com/snippets/css-image-gallery/css-ken-burns-slideshow-gallery/

A cinematic auto-playing slideshow where each image slowly zooms and pans (the 'Ken Burns' effect) while cross-fading to the next — the high-production hero slideshow documentary sites and landing pages search for. Pure CSS keyframes.
## HTML
```html
<section class="gl-19" aria-label="Ken Burns slideshow" aria-roledescription="carousel">
  <div class="gl-19__frame">
    <figure class="gl-19__slide gl-19__slide--1"><img src="https://loremflickr.com/1200/700/mountain,cinematic?lock=1901" alt="Sweeping mountain vista" loading="lazy" decoding="async"><figcaption>The High Passes</figcaption></figure>
    <figure class="gl-19__slide gl-19__slide--2"><img src="https://loremflickr.com/1200/700/ocean,cinematic?lock=1902" alt="Open ocean at golden hour" loading="lazy" decoding="async"><figcaption>Endless Blue</figcaption></figure>
    <figure class="gl-19__slide gl-19__slide--3"><img src="https://loremflickr.com/1200/700/desert,cinematic?lock=1903" alt="Desert at dusk" loading="lazy" decoding="async"><figcaption>The Dry Lands</figcaption></figure>
    <figure class="gl-19__slide gl-19__slide--4"><img src="https://loremflickr.com/1200/700/forest,cinematic?lock=1904" alt="Ancient forest interior" loading="lazy" decoding="async"><figcaption>Old Growth</figcaption></figure>
  </div>
</section>
```
## CSS
```css
.gl-19,
.gl-19 *,
.gl-19 *::before,
.gl-19 *::after {
  box-sizing: border-box;
  margin: 0;
  padding: 0;
}

.gl-19 ::selection {
  background: oklch(0.7 0.13 60);
  color: #111;
}

.gl-19 {
  font-family: 'Segoe UI',system-ui,sans-serif;
  width: 100%;
  min-height: 100vh;
  padding: 40px 20px;
  background: #0a0a0c;
  display: flex;
  align-items: center;
  justify-content: center;
}

.gl-19__frame {
  position: relative;
  width: min(920px,100%);
  aspect-ratio: 12/7;
  border-radius: 16px;
  overflow: hidden;
  box-shadow: 0 40px 80px -40px rgba(0,0,0,.9);
}

.gl-19__slide {
  position: absolute;
  inset: 0;
  opacity: 0;
  animation: gl-19-fade 24s infinite;
}

.gl-19__slide img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  transform-origin: center;
  animation: gl-19-kb 24s infinite;
}

.gl-19__slide figcaption {
  position: absolute;
  left: 0;
  bottom: 0;
  padding: 28px 30px;
  color: #fff;
  font-family: Georgia,serif;
  font-size: 26px;
  font-weight: 700;
  letter-spacing: -.01em;
  background: linear-gradient(0deg,rgba(0,0,0,.6),transparent);
  width: 100%;
}

.gl-19__slide--1 {
  animation-delay: 0s;
}

.gl-19__slide--1 img {
  animation-delay: 0s;
}

.gl-19__slide--2 {
  animation-delay: 6s;
}

.gl-19__slide--2 img {
  animation-delay: 6s;
}

.gl-19__slide--3 {
  animation-delay: 12s;
}

.gl-19__slide--3 img {
  animation-delay: 12s;
}

.gl-19__slide--4 {
  animation-delay: 18s;
}

.gl-19__slide--4 img {
  animation-delay: 18s;
}

@keyframes gl-19-fade {
  0% {
    opacity: 0;
  }

  2% {
    opacity: 1;
  }

  23% {
    opacity: 1;
  }

  27% {
    opacity: 0;
  }

  100% {
    opacity: 0;
  }
}

@keyframes gl-19-kb {
  0% {
    transform: scale(1) translate(0,0);
  }

  27% {
    transform: scale(1.18) translate(-3%,2%);
  }

  100% {
    transform: scale(1.18) translate(-3%,2%);
  }
}

@media (prefers-reduced-motion: reduce) {
  .gl-19__slide,
    .gl-19__slide img {
    animation: none;
  }

  .gl-19__slide--1 {
    opacity: 1;
  }

  .gl-19__slide--1 img {
    transform: scale(1.05);
  }
}
```

## JavaScript
```js
/* No JavaScript — staggered opacity cross-fade + a slow transform pan per slide. */
```

How this works

Slides are stacked absolutely in one frame. A cross-fade @keyframes cycles each slide's opacity in sequence using staggered animation-delays, so one image fades out as the next fades in. Simultaneously each slide's <img> runs a slow transform: scale()/translate() keyframe — the Ken Burns pan-and-zoom — giving static photos gentle cinematic motion.

A caption cross-fades with its slide. Under prefers-reduced-motion the animation stops on the first slide, so the gallery degrades to a calm static hero.

Make it yours

  • Set per-slide time by the total duration ÷ slide count; adjust delays to match.
  • Tune the drama with the image keyframe's scale/translate range.
  • Alternate pan direction per slide (zoom-in on odd, zoom-out on even) for variety.
  • Add captions/CTAs that fade in with each slide.

Gotchas — read before shipping

  • Keep the fade windows and delays consistent across slides or the cross-fade stutters or shows gaps.
  • Animate only opacity/transform; the frame's overflow:hidden contains the pan so nothing spills.
  • Provide the reduced-motion static fallback — auto-advancing, moving imagery must be defeatable.

Browser support

ChromeSafariFirefoxEdge
111+15.4+113+111+

Floor set by oklch() as this demo is written. The core technique itself goes back further — Chrome 43+.

Keyframe opacity cross-fade + transform pan — universally supported.

Techniques used in this demo

Search CodeFronts

Loading…