22 CSS Image Carousels20 / 22
Autoplay CSS Slider
A hands-off carousel that advances itself, translating a single track through each slide on a CSS keyframe loop and pausing the moment a user hovers or focuses it. The lightweight auto-rotating banner — zero JavaScript, zero timers.
Published
The code
<section class="car-20" aria-label="Auto-rotating banner" aria-roledescription="carousel">
<div class="car-20__viewport">
<div class="car-20__track">
<div class="car-20__slide"><img src="https://picsum.photos/seed/auto-1/1000/500" width="1000" height="500" alt="Summer sale banner"></div>
<div class="car-20__slide"><img src="https://picsum.photos/seed/auto-2/1000/500" width="1000" height="500" alt="New arrivals banner"></div>
<div class="car-20__slide"><img src="https://picsum.photos/seed/auto-3/1000/500" width="1000" height="500" alt="Free shipping banner"></div>
</div>
<div class="car-20__bars" aria-hidden="true"><span></span><span></span><span></span></div>
</div>
</section><section class="car-20" aria-label="Auto-rotating banner" aria-roledescription="carousel">
<div class="car-20__viewport">
<div class="car-20__track">
<div class="car-20__slide"><img src="https://picsum.photos/seed/auto-1/1000/500" width="1000" height="500" alt="Summer sale banner"></div>
<div class="car-20__slide"><img src="https://picsum.photos/seed/auto-2/1000/500" width="1000" height="500" alt="New arrivals banner"></div>
<div class="car-20__slide"><img src="https://picsum.photos/seed/auto-3/1000/500" width="1000" height="500" alt="Free shipping banner"></div>
</div>
<div class="car-20__bars" aria-hidden="true"><span></span><span></span><span></span></div>
</div>
</section>.car-20,
.car-20 *,
.car-20 *::before,
.car-20 *::after {
box-sizing: border-box;
margin: 0;
padding: 0;
}
.car-20 ::selection {
background: #db2777;
color: #fff;
}
.car-20 {
--accent: #db2777;
font-family: 'Segoe UI',system-ui,sans-serif;
width: 100%;
min-height: 100vh;
padding: 40px 20px;
background: #1a0f18;
display: flex;
align-items: center;
justify-content: center;
}
.car-20__viewport {
position: relative;
width: min(680px,100%);
border-radius: 16px;
overflow: hidden;
box-shadow: 0 26px 50px -32px rgba(0,0,0,.9);
}
.car-20__track {
display: flex;
width: 300%;
animation: car-20-slide 15s infinite;
}
.car-20__viewport:hover .car-20__track,
.car-20__viewport:focus-within .car-20__track,
.car-20__viewport:hover .car-20__bars span,
.car-20__viewport:focus-within .car-20__bars span {
animation-play-state: paused;
}
.car-20__slide {
width: calc(100%/3);
aspect-ratio: 2/1;
}
.car-20__slide img {
width: 100%;
height: 100%;
object-fit: cover;
display: block;
}
.car-20__bars {
position: absolute;
left: 14px;
right: 14px;
bottom: 14px;
display: flex;
gap: 8px;
}
.car-20__bars span {
flex: 1;
height: 4px;
border-radius: 999px;
background: rgba(255,255,255,.35);
overflow: hidden;
position: relative;
}
.car-20__bars span::after {
content: "";
position: absolute;
inset: 0;
background: var(--accent);
transform-origin: left;
transform: scaleX(0);
}
.car-20__bars span:nth-child(1)::after {
animation: car-20-bar 15s infinite;
animation-delay: 0s;
}
.car-20__bars span:nth-child(2)::after {
animation: car-20-bar 15s infinite;
animation-delay: -10s;
}
.car-20__bars span:nth-child(3)::after {
animation: car-20-bar 15s infinite;
animation-delay: -5s;
}
@keyframes car-20-slide {
0%,
28% {
transform: translateX(0);
}
33%,
61% {
transform: translateX(-33.3333%);
}
66%,
94% {
transform: translateX(-66.6666%);
}
100% {
transform: translateX(0);
}
}
@keyframes car-20-bar {
0% {
transform: scaleX(0);
}
28% {
transform: scaleX(1);
}
33%,
100% {
transform: scaleX(0);
}
}
@media (prefers-reduced-motion: reduce) {
.car-20__track {
animation: none;
}
.car-20__bars {
display: none;
}
}.car-20,
.car-20 *,
.car-20 *::before,
.car-20 *::after {
box-sizing: border-box;
margin: 0;
padding: 0;
}
.car-20 ::selection {
background: #db2777;
color: #fff;
}
.car-20 {
--accent: #db2777;
font-family: 'Segoe UI',system-ui,sans-serif;
width: 100%;
min-height: 100vh;
padding: 40px 20px;
background: #1a0f18;
display: flex;
align-items: center;
justify-content: center;
}
.car-20__viewport {
position: relative;
width: min(680px,100%);
border-radius: 16px;
overflow: hidden;
box-shadow: 0 26px 50px -32px rgba(0,0,0,.9);
}
.car-20__track {
display: flex;
width: 300%;
animation: car-20-slide 15s infinite;
}
.car-20__viewport:hover .car-20__track,
.car-20__viewport:focus-within .car-20__track,
.car-20__viewport:hover .car-20__bars span,
.car-20__viewport:focus-within .car-20__bars span {
animation-play-state: paused;
}
.car-20__slide {
width: calc(100%/3);
aspect-ratio: 2/1;
}
.car-20__slide img {
width: 100%;
height: 100%;
object-fit: cover;
display: block;
}
.car-20__bars {
position: absolute;
left: 14px;
right: 14px;
bottom: 14px;
display: flex;
gap: 8px;
}
.car-20__bars span {
flex: 1;
height: 4px;
border-radius: 999px;
background: rgba(255,255,255,.35);
overflow: hidden;
position: relative;
}
.car-20__bars span::after {
content: "";
position: absolute;
inset: 0;
background: var(--accent);
transform-origin: left;
transform: scaleX(0);
}
.car-20__bars span:nth-child(1)::after {
animation: car-20-bar 15s infinite;
animation-delay: 0s;
}
.car-20__bars span:nth-child(2)::after {
animation: car-20-bar 15s infinite;
animation-delay: -10s;
}
.car-20__bars span:nth-child(3)::after {
animation: car-20-bar 15s infinite;
animation-delay: -5s;
}
@keyframes car-20-slide {
0%,
28% {
transform: translateX(0);
}
33%,
61% {
transform: translateX(-33.3333%);
}
66%,
94% {
transform: translateX(-66.6666%);
}
100% {
transform: translateX(0);
}
}
@keyframes car-20-bar {
0% {
transform: scaleX(0);
}
28% {
transform: scaleX(1);
}
33%,
100% {
transform: scaleX(0);
}
}
@media (prefers-reduced-motion: reduce) {
.car-20__track {
animation: none;
}
.car-20__bars {
display: none;
}
}/* No JavaScript — a single keyframe steps the track's translateX through each slide; hover/focus pauses playback and the progress bars. */
/* No JavaScript — a single keyframe steps the track's translateX through each slide; hover/focus pauses playback and the progress bars. */Here's a working CSS Image Carousel 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: Autoplay CSS Slider
Source: https://codefronts.com/snippets/css-image-carousel/autoplay-css-slider/
A hands-off carousel that advances itself, translating a single track through each slide on a CSS keyframe loop and pausing the moment a user hovers or focuses it. The lightweight auto-rotating banner — zero JavaScript, zero timers.
## HTML
```html
<section class="car-20" aria-label="Auto-rotating banner" aria-roledescription="carousel">
<div class="car-20__viewport">
<div class="car-20__track">
<div class="car-20__slide"><img src="https://picsum.photos/seed/auto-1/1000/500" width="1000" height="500" alt="Summer sale banner"></div>
<div class="car-20__slide"><img src="https://picsum.photos/seed/auto-2/1000/500" width="1000" height="500" alt="New arrivals banner"></div>
<div class="car-20__slide"><img src="https://picsum.photos/seed/auto-3/1000/500" width="1000" height="500" alt="Free shipping banner"></div>
</div>
<div class="car-20__bars" aria-hidden="true"><span></span><span></span><span></span></div>
</div>
</section>
```
## CSS
```css
.car-20,
.car-20 *,
.car-20 *::before,
.car-20 *::after {
box-sizing: border-box;
margin: 0;
padding: 0;
}
.car-20 ::selection {
background: #db2777;
color: #fff;
}
.car-20 {
--accent: #db2777;
font-family: 'Segoe UI',system-ui,sans-serif;
width: 100%;
min-height: 100vh;
padding: 40px 20px;
background: #1a0f18;
display: flex;
align-items: center;
justify-content: center;
}
.car-20__viewport {
position: relative;
width: min(680px,100%);
border-radius: 16px;
overflow: hidden;
box-shadow: 0 26px 50px -32px rgba(0,0,0,.9);
}
.car-20__track {
display: flex;
width: 300%;
animation: car-20-slide 15s infinite;
}
.car-20__viewport:hover .car-20__track,
.car-20__viewport:focus-within .car-20__track,
.car-20__viewport:hover .car-20__bars span,
.car-20__viewport:focus-within .car-20__bars span {
animation-play-state: paused;
}
.car-20__slide {
width: calc(100%/3);
aspect-ratio: 2/1;
}
.car-20__slide img {
width: 100%;
height: 100%;
object-fit: cover;
display: block;
}
.car-20__bars {
position: absolute;
left: 14px;
right: 14px;
bottom: 14px;
display: flex;
gap: 8px;
}
.car-20__bars span {
flex: 1;
height: 4px;
border-radius: 999px;
background: rgba(255,255,255,.35);
overflow: hidden;
position: relative;
}
.car-20__bars span::after {
content: "";
position: absolute;
inset: 0;
background: var(--accent);
transform-origin: left;
transform: scaleX(0);
}
.car-20__bars span:nth-child(1)::after {
animation: car-20-bar 15s infinite;
animation-delay: 0s;
}
.car-20__bars span:nth-child(2)::after {
animation: car-20-bar 15s infinite;
animation-delay: -10s;
}
.car-20__bars span:nth-child(3)::after {
animation: car-20-bar 15s infinite;
animation-delay: -5s;
}
@keyframes car-20-slide {
0%,
28% {
transform: translateX(0);
}
33%,
61% {
transform: translateX(-33.3333%);
}
66%,
94% {
transform: translateX(-66.6666%);
}
100% {
transform: translateX(0);
}
}
@keyframes car-20-bar {
0% {
transform: scaleX(0);
}
28% {
transform: scaleX(1);
}
33%,
100% {
transform: scaleX(0);
}
}
@media (prefers-reduced-motion: reduce) {
.car-20__track {
animation: none;
}
.car-20__bars {
display: none;
}
}
```
## JavaScript
```js
/* No JavaScript — a single keyframe steps the track's translateX through each slide; hover/focus pauses playback and the progress bars. */
```Here's a working CSS Image Carousel 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: Autoplay CSS Slider
Source: https://codefronts.com/snippets/css-image-carousel/autoplay-css-slider/
A hands-off carousel that advances itself, translating a single track through each slide on a CSS keyframe loop and pausing the moment a user hovers or focuses it. The lightweight auto-rotating banner — zero JavaScript, zero timers.
## HTML
```html
<section class="car-20" aria-label="Auto-rotating banner" aria-roledescription="carousel">
<div class="car-20__viewport">
<div class="car-20__track">
<div class="car-20__slide"><img src="https://picsum.photos/seed/auto-1/1000/500" width="1000" height="500" alt="Summer sale banner"></div>
<div class="car-20__slide"><img src="https://picsum.photos/seed/auto-2/1000/500" width="1000" height="500" alt="New arrivals banner"></div>
<div class="car-20__slide"><img src="https://picsum.photos/seed/auto-3/1000/500" width="1000" height="500" alt="Free shipping banner"></div>
</div>
<div class="car-20__bars" aria-hidden="true"><span></span><span></span><span></span></div>
</div>
</section>
```
## CSS
```css
.car-20,
.car-20 *,
.car-20 *::before,
.car-20 *::after {
box-sizing: border-box;
margin: 0;
padding: 0;
}
.car-20 ::selection {
background: #db2777;
color: #fff;
}
.car-20 {
--accent: #db2777;
font-family: 'Segoe UI',system-ui,sans-serif;
width: 100%;
min-height: 100vh;
padding: 40px 20px;
background: #1a0f18;
display: flex;
align-items: center;
justify-content: center;
}
.car-20__viewport {
position: relative;
width: min(680px,100%);
border-radius: 16px;
overflow: hidden;
box-shadow: 0 26px 50px -32px rgba(0,0,0,.9);
}
.car-20__track {
display: flex;
width: 300%;
animation: car-20-slide 15s infinite;
}
.car-20__viewport:hover .car-20__track,
.car-20__viewport:focus-within .car-20__track,
.car-20__viewport:hover .car-20__bars span,
.car-20__viewport:focus-within .car-20__bars span {
animation-play-state: paused;
}
.car-20__slide {
width: calc(100%/3);
aspect-ratio: 2/1;
}
.car-20__slide img {
width: 100%;
height: 100%;
object-fit: cover;
display: block;
}
.car-20__bars {
position: absolute;
left: 14px;
right: 14px;
bottom: 14px;
display: flex;
gap: 8px;
}
.car-20__bars span {
flex: 1;
height: 4px;
border-radius: 999px;
background: rgba(255,255,255,.35);
overflow: hidden;
position: relative;
}
.car-20__bars span::after {
content: "";
position: absolute;
inset: 0;
background: var(--accent);
transform-origin: left;
transform: scaleX(0);
}
.car-20__bars span:nth-child(1)::after {
animation: car-20-bar 15s infinite;
animation-delay: 0s;
}
.car-20__bars span:nth-child(2)::after {
animation: car-20-bar 15s infinite;
animation-delay: -10s;
}
.car-20__bars span:nth-child(3)::after {
animation: car-20-bar 15s infinite;
animation-delay: -5s;
}
@keyframes car-20-slide {
0%,
28% {
transform: translateX(0);
}
33%,
61% {
transform: translateX(-33.3333%);
}
66%,
94% {
transform: translateX(-66.6666%);
}
100% {
transform: translateX(0);
}
}
@keyframes car-20-bar {
0% {
transform: scaleX(0);
}
28% {
transform: scaleX(1);
}
33%,
100% {
transform: scaleX(0);
}
}
@media (prefers-reduced-motion: reduce) {
.car-20__track {
animation: none;
}
.car-20__bars {
display: none;
}
}
```
## JavaScript
```js
/* No JavaScript — a single keyframe steps the track's translateX through each slide; hover/focus pauses playback and the progress bars. */
```How this works
All slides sit in one horizontal track that is N×100% wide. A @keyframes steps the track's translateX through each slide position with hold-then-move stops, looping forever — the autoplay is the CSS animation itself, so there's no setInterval to leak or desync. Translating the track keeps it compositor-bound.
Hovering or focusing the viewport sets animation-play-state:paused, and animated progress bars mirror the current slide. Under prefers-reduced-motion the animation stops on slide one, turning an autoplaying banner into a safe static image for vestibular users.
Make it yours
- Set slide dwell + total loop time via the keyframe stops and
animation-duration. - Add a slide by widening the track (
N×100%) and adding keyframe stops. - Swap the hard step for a continuous drift by easing the keyframe.
- Restyle or remove the progress indicators.
Gotchas — read before shipping
- Keep keyframe percentages evenly divided by slide count, or timing drifts.
- Always pause on hover/focus and honour reduced-motion — forced perpetual motion hurts INP and accessibility.
- Make the track width an exact multiple of 100% so each slide lands flush.
Browser support
| Chrome | Safari | Firefox | Edge |
|---|---|---|---|
| 43+ | 9+ | 48+ | 43+ |
Keyframe translate track; no modern-only features.