20 CSS Image Sliders05 / 20
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
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><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;
}
}.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. */
/* No JavaScript — one @keyframes translateX loop over a doubled image strip. */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. */
```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: reverseon.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
--fademask 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
| Chrome | Safari | Firefox | Edge |
|---|---|---|---|
| 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.