You might be thinking “CSS is used for styling while JavaScript is used for interactivity, that’s just the way the web works.”
But CSS loads on a webpage faster than JavaScript. And CSS also causes less reflow on a page (which improves performance). So, it’s fair to say we should use CSS for interactivity wherever possible.
1. Layout with HTML
The layout of our easy slider is simple. We’ll create a carousel-container
div to hold the carousel-slide
elements.
1 |
|
2 |
|
3 |
... |
4 |
|
That’s all we need so let’s move on to the styling of our carousel in HTML and CSS.
2. CSS Carousel behaviour
Once we have our carousel slides set up, we’ll style the CSS slider to have the following features:
- Scrollable content
- Snap to next slide
- Progress bar indicating slide progress
Scrollable content
For the scrollable content, we’ll use the display:flex
and overflow-x: auto
properties. We’ll also style slides so we can see 3 slides on the desktop screen and 1 slide on mobile.
1 |
.carousel-container { |
2 |
display: flex; |
3 |
overflow-x: auto; |
4 |
}
|
5 |
|
6 |
.carousel-slide { |
7 |
flex: 1 0 30%; |
8 |
}
|
9 |
|
10 |
@media (max-width: 600px) { |
11 |
.carousel-slide { |
12 |
flex: 1 0 90%; |
13 |
}
|
14 |
}
|
Snap to slide
Next, to achieve the snapping effect on the slides, we’ll use the CSS scroll-snap properties.
The scroll snap properties allow us define “snapping” points on an element. These are the points of the element that we want to be fully visible once scrolling.
The updated code of our carousel in CSS looks like this:
1 |
.carousel-container { |
2 |
display: flex; |
3 |
overflow-x: auto; |
4 |
scroll-snap-type: x mandatory; |
5 |
}
|
6 |
|
7 |
.carousel-slide { |
8 |
flex: 1 0 30%; |
9 |
scroll-snap-align: center; |
10 |
}
|
11 |
|
12 |
@media (max-width: 600px) { |
13 |
.carousel-slide { |
14 |
flex: 1 0 90%; |
15 |
}
|
16 |
}
|
Optional: CSS-only progress bar
Let’s keep inline with our CSS-only interactivity. We can take advantage of native browser features to create a progress bar for our carousel in CSS.
We’ll do this by styling the carousel container scrollbar to give the appearance of a progress bar. This is what the code looks like:
1 |
.carousel-container::-webkit-scrollbar { |
2 |
height: 8px; |
3 |
}
|
4 |
|
5 |
.carousel-container::-webkit-scrollbar-thumb { |
6 |
background: #29AB87; |
7 |
}
|
8 |
|
9 |
.carousel-container::-webkit-scrollbar-track { |
10 |
background: #b1b3b399; |
11 |
}
|
12 |
|
13 |
.carousel-container::-webkit-scrollbar-track-piece:start { |
14 |
background: #29AB87; |
15 |
}
|
Let’s look at the properties we’re using:
-
::webkit-scrollbar
: the entire scrollbar element -
::webkit-scrollbar-thumb
: the draggable bar on the scrollbar -
::webkit-scrollbar-track
: the path that the scrollbar thumb is on -
::webkit-scrollbar-track-piece:start
: the path of the track not covered by the scrollbar thumb, the:start
CSS selector targets only the path behind the scrollbar thumb


In the diagram above, we can see what parts of the scrollbar are being targeted. By making the -webkit-scrollbar-thumb
and ::webkit-scrollbar-track-piece:start
the same color in CSS, the scrollbar gives the impression of being filled in as it’s being scrolled. This creates a progress bar.
Since our progress bar is actually a scrollbar, it can also be used to scroll through the CSS carousel. It results in an additional feature: it’s a win/win!
The ::webkit-scrollbar
properties are non-standard, pretty sketchy, and not supported by all browsers. That’s why it’s not recommended to use this in a production environment. Our progress bar will look like a regular scrollbar on non-supported browsers. This also happens if you choose not to include these ::webkit-scrollbar
rules.
That’s all there is to our easy slider! We’ve built a scrollable container with a nifty snapping feature. We even added a progress bar using only CSS:
3. More features with JavaScript
We’ve gotten the basic feature of the CSS carousel out of the way. Now we can go on to add even more features, using JavaScript this time.
One of those features is using arrows to handle the carousel navigation. In a previous tutorial, we looked into building a carousel with JavaScript (less than 14 lines of code!). We can combine that tutorial with this one to add even more features to our easy slider.
This is what our combined CSS carousel looks like:
In this demo of a carousel in CSS and HTML, just for fun, the code has been refactored to use even fewer lines of JavaScript. This is what our updated carousel arrow function looks like:
1 |
const carousel = document.querySelector(".carousel-container"); |
2 |
const slide = document.querySelector(".carousel-slide"); |
3 |
|
4 |
function handleCarouselMove(positive = true) { |
5 |
const slideWidth = slide.clientWidth; |
6 |
carousel.scrollLeft = positive ? carousel.scrollLeft + slideWidth : carousel.scrollLeft - slideWidth; |
7 |
}
|
Then we pass that function to our HTML arrows:
1 |
|
2 |
‹
|
3 |
|
4 |
|
5 |
|
6 |
›
|
7 |
|
And with that, we’ll call it a day on our modded-up carousel in CSS and HTML!
What’s next?
You just learned how to create an easy carousel in HTML and CSS. There’s a ton of useful tips and tricks yet for you to learn. Check out some of our tutorials and get closer to become a CSS master!