Super Simple Progressively Enhanced Carousel with CSS Scroll Snap
In this article, we explore how to create a simple carousel with only HTML and CSS. Recently, when I was reminded of the existence of the CSS property scroll-snap
, I thought it should be easy to create a simple carousel component with it. After outlining a quick proof of concept in a simple HTML file, my assumption was confirmed.
Pure HTML and CSS solution
In the past, I absolutely loved to implement things with pure HTML and CSS for which other people used JavaScript. Things like the checkbox hack come to my mind. It wasn’t always very practical to do it that way, but it was fun. But in this case, it makes absolute sense to implement a simple carousel with pure HTML and CSS.
<div class="carousel">
<div id="skyline" class="carousel__item">
<img src="..." alt="..." class="carousel__image" />
</div>
<div id="great-wall-of-china" class="carousel__item">
<img src="..." alt="..." class="carousel__image" />
</div>
<div id="sunset-on-the-li-river" class="carousel__item">
<img src="..." alt="..." class="carousel__image" />
</div>
</div>
<div id="controls" class="controls">
<a href="#skyline" class="controls__dot">
<span class="visuallyhidden">Skyline of Wai Tan, Shanghai</span>
</a>
<a href="#great-wall-of-china" class="controls__dot">
<span class="visuallyhidden">Great wall of China</span>
</a>
<a href="#sunset-on-the-li-river" class="controls__dot">
<span class="visuallyhidden">Sunset on the Li River</span>
</a>
</div>
.carousel {
display: flex;
scroll-snap-type: x mandatory;
overflow-x: scroll;
scroll-behavior: smooth;
}
.carousel__item {
width: 100%;
flex-shrink: 0;
scroll-snap-align: start;
}
.carousel__image {
display: block;
}
// Styles for controls omitted because you
// can style them however you want.
In the example above, you can see all the necessary parts we need to make everything work. Have a look at the following demo to see how far this little code takes us.
See the Pen CSS Scroll Snap Carousel (1) by Markus Oberlehner (@maoberlehner) on CodePen.
Hiding the scrollbar despite overflow: scroll
The solution works pretty well, but although there are advantages and disadvantages to having the scrollbar visible, it’s not very pretty. At least not in operating systems that show a scrollbar by default. But luckily, there is a rather simple trick for hiding the scrollbar with CSS.
.carousel {
// ...
// Hide scrollbar in IE.
-ms-overflow-style: none;
}
// Hide scrollbar in WebKit and Blink powered browsers.
.carousel::-webkit-scrollbar {
display: none;
}
As you can see above, by using the ::-webkit-scrollbar
pseudo selector for WebKit and Blink powered browsers and -ms-overflow-style: none
for IE, there is no scrollbar anymore in most browsers.
See the Pen Super Simple Progressively Enhanced Carousel with CSS Scroll Snap by Markus Oberlehner (@maoberlehner) on CodePen.
Progressive Enhancement with JavaScript
Our current solution already works (almost) perfectly fine with pure CSS and HTML. But there is one annoying thing about it: when you click an anchor link to skip to the next carousel item, the browser scrolls not only horizontally but also vertically. Luckily we can make this a little less annoying in situations where the carousel already is fully visible.
document.querySelector("#controls").addEventListener("click", (event) => {
const $slide = document.querySelector(event.target.getAttribute("href"));
if (!$slide) return;
if ($slide.scrollIntoViewIfNeeded) {
event.preventDefault();
$slide.scrollIntoViewIfNeeded();
} else if ($slide.scrollIntoView) {
event.preventDefault();
$slide.scrollIntoView();
}
});
Wrapping it up
Thanks to modern features like scroll-snap
, we don’t have to reach for a massive library if we want to create a simple carousel for our website. This solution also works even if the loading or execution of JavaScript fails for some reason. What’s more, is that this also works for older browsers that don’t support scroll-snap
. Of course, the solution is not perfect in those browsers, but it’s usable. That’s the spirit of progressive enhancement.