Looping images and icons infinitely using CSS only

Dominik Rüttiger 3 min read

Slider image 1Slider image 2Slider image 3Slider image 4Slider image 5Slider image 6Slider image 7Slider image 8

Source: Photos from Unsplash

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.

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.

page.html
<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.

Slider.astro
---
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>

Tags

#astro #css #webperformance

Share

Twitter LinkedIn Hacker News Reddit