Most carousels run on JavaScript - a library, a few event listeners, some state to track which slide you’re on. A lot of the time that’s more than the job needs. If all you want is a row of slides someone can swipe through, CSS can do the whole thing on its own with scroll-snap.
Here’s one built from nothing but CSS and a handful of divs. Swipe it on a phone, or drag with a trackpad:
No JavaScript anywhere. Here’s how it works.
The markup
Just a container and some slides:
<div class="slides-container">
<div class="slide">1</div>
<div class="slide">2</div>
<div class="slide">3</div>
<div class="slide">4</div>
<div class="slide">5</div>
<div class="slide">6</div>
</div>
That’s the whole structure. Everything else is CSS.
The idea
Four properties do the heavy lifting:
- A flex row that doesn’t wrap, so the slides line up side by side instead of stacking.
- Horizontal scrolling on the container.
scroll-snap-typeon the container, so a scroll settles neatly on a slide instead of stopping halfway.scroll-snap-stopon each slide, so one swipe moves one slide rather than flinging past several.
The CSS
.slides-container {
display: flex;
flex-flow: row nowrap; /* one row, no wrapping */
overflow-x: auto; /* let it scroll sideways */
scroll-snap-type: x mandatory; /* always settle on a slide */
gap: 20px;
height: 200px;
scrollbar-width: none; /* hide the scrollbar in Firefox */
}
.slides-container::-webkit-scrollbar {
display: none; /* and in Chrome / Safari */
}
.slides-container .slide {
flex: none; /* keep each slide full width - don't shrink */
width: 100%;
height: 100%;
scroll-snap-align: center; /* snap to the slide's center */
scroll-snap-stop: always; /* one slide per swipe, no skipping */
display: flex;
align-items: center;
justify-content: center;
background: skyblue;
color: #fff;
font-size: 80px;
font-weight: bold;
}
A few of those lines are worth calling out:
flex: noneon the slides keeps each one at full width. Without it, flex would happily shrink them to fit, the row would never overflow, and there’d be nothing to scroll.scroll-snap-type: x mandatorytells the browser to always snap to a slide on the x-axis. Pair it withscroll-snap-align: centerand each slide settles dead center.scroll-snap-stop: alwaysis the one people miss. Without it, a hard swipe can blow past two or three slides at once. With it, every swipe advances exactly one.- Hiding the scrollbar is purely cosmetic - the scrolling still works, the bar’s just gone.
The one thing it won’t do
Because it’s built on scrolling, it responds to everything that scrolls: touch, trackpad, the scrollbar, even the arrow keys when it’s focused. The one gesture it doesn’t handle on its own is grabbing it with the mouse and dragging - click-and-drag needs a little JavaScript. For most carousels, nobody misses it.
When to reach for it
For image strips, logo rows, a row of testimonials - anything where swiping is the natural gesture - this is hard to beat. There’s no dependency to install, nothing to keep updated, and almost nothing to ship. Leaning on the platform like this is a big part of how you keep a site fast. Save the JavaScript for when you genuinely need it: autoplay, arrow buttons that jump to an exact slide, or that mouse-drag.



