Looping images and icons infinitely using CSS only
Dominik Rüttiger
I was looking for a CSS-only infinite carousel that was easy to use, easy to understand and can serve a variable number of images. It should not depend on SAAS or other tools and work for any type of content.
And here it is! Let’s go through a plain HTML example and integrate it into an Astro component.
CSS-only infinite carousel
The HTML is pretty straightforward: Two wrapping divs containing a list of images (or whatever you want to loop over). We duplicate the list for a seamless loop.
The .slides
CSS rule lays out all the images in a row using flex
and
adds a custom animation. Thanks to width: max-content
, we can simply move them
by -50%
. No need for pixel-based calculations.
The carousel stops when you move the mouse over it and a mask is applied. If you
don’t need this, just remove &:hover
and -webkit-mask
.
<div class="slider"> <div class="slides"> <img src="https://images.unsplash.com/photo-1511992243105-2992b3fd0410?w=300&h=200&auto=format&fit=crop" alt="Photo by Alexander Andrews on Unsplash" /> <img src="https://images.unsplash.com/photo-1551232865-e0a56728e881?w=300&h=200&auto=format&fit=crop" alt="Photo by Amanda Vick on Unsplash" /> <img src="https://images.unsplash.com/photo-1515468566430-e419942dd6f6?w=300&h=200&auto=format&fit=crop" alt="Photo by Jason Leung on Unsplash" /> <img src="https://images.unsplash.com/photo-1465158154535-2ef77cee33f3?w=300&h=200&auto=format&fit=crop" alt="Photo by Nathan Anderson on Unsplash" /> <!-- Repeat images --> <img src="https://images.unsplash.com/photo-1511992243105-2992b3fd0410?w=300&h=200&auto=format&fit=crop" alt="Photo by Alexander Andrews on Unsplash" /> <img src="https://images.unsplash.com/photo-1551232865-e0a56728e881?w=300&h=200&auto=format&fit=crop" alt="Photo by Amanda Vick on Unsplash" /> <img src="https://images.unsplash.com/photo-1515468566430-e419942dd6f6?w=300&h=200&auto=format&fit=crop" alt="Photo by Jason Leung on Unsplash" /> <img src="https://images.unsplash.com/photo-1465158154535-2ef77cee33f3?w=300&h=200&auto=format&fit=crop" alt="Photo by Nathan Anderson on Unsplash" /> </div></div>
<style> .slider { overflow: hidden; -webkit-mask: linear-gradient(90deg, #0000, #000 5% 95%, #0000); }
.slides { display: flex; width: max-content; animation: slider-slide-left 20s linear infinite; }
.slides:hover { animation-play-state: paused; }
.slides img { margin-right: 50px; }
@keyframes slider-slide-left { 100% { transform: translateX(-50%); } }</style>
Astro component
For the Astro version, let’s add some image optimizations to make your Lighthouse audits fireworks!
We make each image take up 50 % of the screen for small displays up to a maximum of 300 pixels. Loading images eagerly helps to avoid glitches in some browsers.
Images are also cropped to fit a 3:2 aspect ratio.
---import type { ImageMetadata } from 'astro';import { Image } from 'astro:assets';
interface Props { images: ImageMetadata[]; duration?: string;}
const { images, duration = '30s' } = Astro.props;---
<div class="slider"> <div class="slides"> { images .concat(images) .map((img, i) => ( <Image class="slide" src={img} alt={`Slider image ${i + 1}`} widths={[300 / 2, 300, 300 * 1.5, 300 * 2]} sizes="(max-width: 625px) 50vw, 300px" loading="eager" decoding="auto" /> )) } </div></div>
<style define:vars={{ duration }}> .slider { overflow: hidden; -webkit-mask: linear-gradient(90deg, #0000, #000 5% 95%, #0000); }
.slides { display: flex; width: max-content; animation: slider-slide-left var(--duration) linear infinite; }
.slides:hover { animation-play-state: paused; }
.slide { width: 50vw; max-width: 300px; aspect-ratio: 3 / 2; object-fit: cover; margin-right: 50px; }
@keyframes slider-slide-left { 100% { transform: translateX(-50%); } }</style>