20 CSS Image Sliders05 / 20

Pure CSSMIT licensed

Automatic Infinite-Loop Banner Slider

Automatic infinite-loop CSS banner slider that runs forever from one @keyframes transform — perfect for hero strips, promotional banners, and scrolling gallery marquees. Feathered mask edges hide the loop seam, pauses on hover, and honours prefers-reduced-motion. No JavaScript, no autoplay interval hack, no visible reset frame.

Published

Live Demo
Try it

The code

<div class="cis-05">
  <div class="cis-05__stage" aria-label="Auto-scrolling image banner">
    <div class="cis-05__marquee">
      <figure class="cis-05__item"><img src="https://picsum.photos/seed/cis05a/600/400" alt="Studio still life"></figure>
      <figure class="cis-05__item"><img src="https://picsum.photos/seed/cis05b/600/400" alt="Mountain switchback road"></figure>
      <figure class="cis-05__item"><img src="https://picsum.photos/seed/cis05c/600/400" alt="Coastline from above"></figure>
      <figure class="cis-05__item"><img src="https://picsum.photos/seed/cis05d/600/400" alt="Architectural detail"></figure>
      <figure class="cis-05__item"><img src="https://picsum.photos/seed/cis05e/600/400" alt="Field at golden hour"></figure>
      <figure class="cis-05__item" aria-hidden="true"><img src="https://picsum.photos/seed/cis05a/600/400" alt=""></figure>
      <figure class="cis-05__item" aria-hidden="true"><img src="https://picsum.photos/seed/cis05b/600/400" alt=""></figure>
      <figure class="cis-05__item" aria-hidden="true"><img src="https://picsum.photos/seed/cis05c/600/400" alt=""></figure>
      <figure class="cis-05__item" aria-hidden="true"><img src="https://picsum.photos/seed/cis05d/600/400" alt=""></figure>
      <figure class="cis-05__item" aria-hidden="true"><img src="https://picsum.photos/seed/cis05e/600/400" alt=""></figure>
    </div>
  </div>
  <p class="cis-05__hint">hover to pause</p>
</div>
.cis-05,
.cis-05 *,
.cis-05 *::before,
.cis-05 *::after {
  box-sizing: border-box;
  margin: 0;
  padding: 0;
}

.cis-05 ::selection {
  background: oklch(0.85 0.16 160);
  color: #04140c;
}

.cis-05 {
  --speed: 34s;
  --item: 300px;
  --gap: 18px;
  --fade: 12%;
  font-family: 'Segoe UI',system-ui,sans-serif;
  color: #e8f0ea;
  width: 100%;
  min-height: 100vh;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 16px;
  background: linear-gradient(120deg,#08120c,#0c1a12 55%,#08120c);
}

.cis-05__stage {
  width: min(960px,98vw);
  overflow: hidden;
  -webkit-mask-image: linear-gradient(90deg,transparent,#000 var(--fade),#000 calc(100% - var(--fade)),transparent);
  mask-image: linear-gradient(90deg,transparent,#000 var(--fade),#000 calc(100% - var(--fade)),transparent);
}

.cis-05__marquee {
  display: flex;
  gap: var(--gap);
  width: max-content;
  animation: cis-05-scroll var(--speed) linear infinite;
  will-change: transform;
}

.cis-05__stage:hover .cis-05__marquee {
  animation-play-state: paused;
}

.cis-05__item {
  flex: 0 0 var(--item);
  aspect-ratio: 3/2;
  border-radius: 16px;
  overflow: hidden;
  border: 1px solid rgba(255,255,255,.08);
  box-shadow: 0 20px 40px -24px rgba(0,0,0,.8);
  transition: transform .4s;
}

.cis-05__item:hover {
  transform: translateY(-6px) scale(1.02);
}

.cis-05__item img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  display: block;
}

.cis-05__hint {
  font-size: 11px;
  letter-spacing: .28em;
  text-transform: uppercase;
  color: #5f7a68;
}

@keyframes cis-05-scroll {
  from {
    transform: translateX(0);
  }

  to {
    transform: translateX(-50%);
  }
}

@media (prefers-reduced-motion: reduce) {
  .cis-05__marquee {
    animation: none;
  }

  .cis-05__item {
    transition: none;
  }
}
/* No JavaScript — one @keyframes translateX loop over a doubled image strip. */
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 Slider 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: Automatic Infinite-Loop Banner Slider
Source: https://codefronts.com/components/css-image-slider/automatic-infinite-loop-banner-slider/

Automatic infinite-loop CSS banner slider that runs forever from one @keyframes transform — perfect for hero strips, promotional banners, and scrolling gallery marquees. Feathered mask edges hide the loop seam, pauses on hover, and honours prefers-reduced-motion. No JavaScript, no autoplay interval hack, no visible reset frame.
## HTML
```html
<div class="cis-05">
  <div class="cis-05__stage" aria-label="Auto-scrolling image banner">
    <div class="cis-05__marquee">
      <figure class="cis-05__item"><img src="https://picsum.photos/seed/cis05a/600/400" alt="Studio still life"></figure>
      <figure class="cis-05__item"><img src="https://picsum.photos/seed/cis05b/600/400" alt="Mountain switchback road"></figure>
      <figure class="cis-05__item"><img src="https://picsum.photos/seed/cis05c/600/400" alt="Coastline from above"></figure>
      <figure class="cis-05__item"><img src="https://picsum.photos/seed/cis05d/600/400" alt="Architectural detail"></figure>
      <figure class="cis-05__item"><img src="https://picsum.photos/seed/cis05e/600/400" alt="Field at golden hour"></figure>
      <figure class="cis-05__item" aria-hidden="true"><img src="https://picsum.photos/seed/cis05a/600/400" alt=""></figure>
      <figure class="cis-05__item" aria-hidden="true"><img src="https://picsum.photos/seed/cis05b/600/400" alt=""></figure>
      <figure class="cis-05__item" aria-hidden="true"><img src="https://picsum.photos/seed/cis05c/600/400" alt=""></figure>
      <figure class="cis-05__item" aria-hidden="true"><img src="https://picsum.photos/seed/cis05d/600/400" alt=""></figure>
      <figure class="cis-05__item" aria-hidden="true"><img src="https://picsum.photos/seed/cis05e/600/400" alt=""></figure>
    </div>
  </div>
  <p class="cis-05__hint">hover to pause</p>
</div>
```
## CSS
```css
.cis-05,
.cis-05 *,
.cis-05 *::before,
.cis-05 *::after {
  box-sizing: border-box;
  margin: 0;
  padding: 0;
}

.cis-05 ::selection {
  background: oklch(0.85 0.16 160);
  color: #04140c;
}

.cis-05 {
  --speed: 34s;
  --item: 300px;
  --gap: 18px;
  --fade: 12%;
  font-family: 'Segoe UI',system-ui,sans-serif;
  color: #e8f0ea;
  width: 100%;
  min-height: 100vh;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 16px;
  background: linear-gradient(120deg,#08120c,#0c1a12 55%,#08120c);
}

.cis-05__stage {
  width: min(960px,98vw);
  overflow: hidden;
  -webkit-mask-image: linear-gradient(90deg,transparent,#000 var(--fade),#000 calc(100% - var(--fade)),transparent);
  mask-image: linear-gradient(90deg,transparent,#000 var(--fade),#000 calc(100% - var(--fade)),transparent);
}

.cis-05__marquee {
  display: flex;
  gap: var(--gap);
  width: max-content;
  animation: cis-05-scroll var(--speed) linear infinite;
  will-change: transform;
}

.cis-05__stage:hover .cis-05__marquee {
  animation-play-state: paused;
}

.cis-05__item {
  flex: 0 0 var(--item);
  aspect-ratio: 3/2;
  border-radius: 16px;
  overflow: hidden;
  border: 1px solid rgba(255,255,255,.08);
  box-shadow: 0 20px 40px -24px rgba(0,0,0,.8);
  transition: transform .4s;
}

.cis-05__item:hover {
  transform: translateY(-6px) scale(1.02);
}

.cis-05__item img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  display: block;
}

.cis-05__hint {
  font-size: 11px;
  letter-spacing: .28em;
  text-transform: uppercase;
  color: #5f7a68;
}

@keyframes cis-05-scroll {
  from {
    transform: translateX(0);
  }

  to {
    transform: translateX(-50%);
  }
}

@media (prefers-reduced-motion: reduce) {
  .cis-05__marquee {
    animation: none;
  }

  .cis-05__item {
    transition: none;
  }
}
```

## JavaScript
```js
/* No JavaScript — one @keyframes translateX loop over a doubled image strip. */
```

How this works

The image strip is duplicated back-to-back inside a flex .marquee, then a single @keyframes animates transform: translateX() from 0 to -50%. Because the second half is an exact copy of the first, the reset at -50% is visually identical to 0, so the loop is seamless with no snap.

Everything stays on the compositor (only transform animates), and a mask-image gradient fades both edges so images appear and disappear softly instead of clipping at the frame. :hover sets animation-play-state: paused so visitors can stop on an image, and the whole thing halts under prefers-reduced-motion.

Make it yours

  • Change speed with --speed (animation duration) on .cis-05 — larger is slower.
  • Reverse direction by setting animation-direction: reverse on .cis-05__marquee.
  • Resize items via --item (width) and the gap; keep both duplicate halves in sync.
  • Widen or tighten the edge feather with the --fade mask width.

Gotchas — read before shipping

  • You must duplicate the item set exactly (and keep aria-hidden="true" on the copy) or the loop will visibly jump at reset.
  • The translate distance is -50% precisely because the content is doubled — change the duplication and you must change the keyframe.
  • Provide a pause affordance (hover here, and it stops for reduced-motion users) so an auto-moving banner stays accessible.

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 85+.

Pure transform animation + mask-image; supported across all current browsers.

Techniques used in this demo

Search CodeFronts

Loading…