CSS SCROLL-SNAP CAROUSEL GENERATOR
Tune a snap carousel, swipe it live, then copy the CSS, HTML and script.
In short
Build a swipeable carousel with pure CSS scroll-snap, horizontal or vertical, and try it on a live preview you can swipe and scroll. Choose the number of slides, slide size, gap, snap alignment (start, center or end), mandatory or proximity snapping and optional prev/next buttons, then copy the CSS, HTML and a short script. It runs in your browser and nothing is uploaded.
Swipe, scroll or use the arrow keys on the preview — it snaps like the real thing.
.carousel {
display: flex;
gap: 16px;
overflow-x: auto;
overscroll-behavior-x: contain;
scroll-snap-type: x mandatory;
-webkit-overflow-scrolling: touch;
scrollbar-width: none;
}
.carousel::-webkit-scrollbar {
display: none;
}
/* Spacers let the first and last slide reach the snap position */
.carousel::before,
.carousel::after {
content: "";
flex: 0 0 max(0px, calc(10% - 16px));
}
.carousel > .slide {
flex: 0 0 80%;
scroll-snap-align: center;
scroll-snap-stop: always;
min-height: 220px;
display: grid;
place-items: center;
color: #fff;
font: 600 20px system-ui, sans-serif;
border-radius: 12px;
}
.carousel-wrap {
position: relative;
}
.carousel-btn {
position: absolute;
top: 50%;
transform: translateY(-50%);
width: 40px;
height: 40px;
border: 0;
border-radius: 50%;
background: rgb(0 0 0 / 0.6);
color: #fff;
font-size: 20px;
cursor: pointer;
z-index: 1;
}
.carousel-btn.prev {
left: 8px;
}
.carousel-btn.next {
right: 8px;
}Example output (default settings)
.carousel {
display: flex;
gap: 16px;
overflow-x: auto;
overscroll-behavior-x: contain;
scroll-snap-type: x mandatory;
-webkit-overflow-scrolling: touch;
scrollbar-width: none;
}
.carousel::-webkit-scrollbar {
display: none;
}
/* Spacers let the first and last slide reach the snap position */
.carousel::before,
.carousel::after {
content: "";
flex: 0 0 max(0px, calc(10% - 16px));
}
.carousel > .slide {
flex: 0 0 80%;
scroll-snap-align: center;
scroll-snap-stop: always;
min-height: 220px;
display: grid;
place-items: center;
color: #fff;
font: 600 20px system-ui, sans-serif;
border-radius: 12px;
}
.carousel-wrap {
position: relative;
}
.carousel-btn {
position: absolute;
top: 50%;
transform: translateY(-50%);
width: 40px;
height: 40px;
border: 0;
border-radius: 50%;
background: rgb(0 0 0 / 0.6);
color: #fff;
font-size: 20px;
cursor: pointer;
z-index: 1;
}
.carousel-btn.prev {
left: 8px;
}
.carousel-btn.next {
right: 8px;
}<div class="carousel-wrap">
<button class="carousel-btn prev" type="button" aria-label="Previous slide">‹</button>
<div class="carousel" tabindex="0" aria-label="Carousel">
<div class="slide" style="background: hsl(14 70% 55%)">Slide 1</div>
<div class="slide" style="background: hsl(61 70% 55%)">Slide 2</div>
<div class="slide" style="background: hsl(108 70% 55%)">Slide 3</div>
<div class="slide" style="background: hsl(155 70% 55%)">Slide 4</div>
<div class="slide" style="background: hsl(202 70% 55%)">Slide 5</div>
</div>
<button class="carousel-btn next" type="button" aria-label="Next slide">›</button>
</div>const carousel = document.querySelector(".carousel");
const slides = [...carousel.querySelectorAll(".slide")];
// Index of the slide sitting closest to the snap position
function currentIndex() {
const box = carousel.getBoundingClientRect();
const target = box.left + box.width / 2;
let best = 0;
let bestDistance = Infinity;
slides.forEach((slide, i) => {
const rect = slide.getBoundingClientRect();
const distance = Math.abs(rect.left + rect.width / 2 - target);
if (distance < bestDistance) {
best = i;
bestDistance = distance;
}
});
return best;
}
function go(step) {
const index = Math.min(slides.length - 1, Math.max(0, currentIndex() + step));
slides[index].scrollIntoView({
behavior: matchMedia("(prefers-reduced-motion: reduce)").matches ? "auto" : "smooth",
block: "nearest",
inline: "center",
});
}
document.querySelector(".carousel-btn.prev").addEventListener("click", () => go(-1));
document.querySelector(".carousel-btn.next").addEventListener("click", () => go(1));How to build a scroll-snap carousel
- 1
Swipe, scroll or use the arrow keys on the preview carousel to feel how it snaps. Pick a preset (Centered cards, Full-width slider, Peek next card, Card row, Vertical feed, Free-ish scroll) as a starting point.
- 2
Choose the direction, the number of slides, the slide size as a percentage of the carousel, the gap and the corner radius.
- 3
Set the snap alignment (start, center or end) and how strict snapping is: mandatory always lands on a slide, proximity snaps only when you stop close to one. Turn on "Don't skip slides" to stop fast swipes from flying past slides.
- 4
Decide whether to hide the scrollbar and whether to add prev/next buttons.
- 5
Open the CSS, HTML and JS tabs and copy each into your project, replacing the placeholder slides with your own content. Use Copy link to share your settings.
Questions
- What is CSS scroll-snap?
- Scroll-snap is a set of CSS properties that make a scrolling container come to rest on specific points instead of anywhere. You set scroll-snap-type on the container (for example x mandatory) and scroll-snap-align on the children (start, center or end). The browser handles touch swipes, mouse wheel, trackpad and keyboard scrolling, so a basic carousel needs no JavaScript at all.
- What is the difference between mandatory and proximity snapping?
- With mandatory, the container always ends up resting on a snap point, so a slide is never left half visible. With proximity, the browser only snaps if the scroll position ends near a snap point and otherwise lets you rest anywhere, which feels freer on long lists. A carousel normally uses mandatory; use proximity if the content is tall or you want people to be able to stop between items.
- Why does my carousel skip slides on a fast swipe?
- By default a hard flick can carry the scroll past several snap points. Setting scroll-snap-stop: always on the slides forces the browser to stop at every one of them. The "Don't skip slides" option adds it. It is on by default here because skipped slides are the most common complaint with scroll-snap carousels.
- Why are there empty spacers before the first and after the last slide?
- When slides are smaller than the carousel and snap to the center or end, the first slide cannot reach that position at scroll offset zero, and the last one cannot reach it at the far end. The generated CSS adds ::before and ::after pseudo-elements of the right size, minus the gap, so those slides can be centered too. With start alignment or full-width slides no spacers are needed and none are generated.
- How do the prev and next buttons work without a library?
- The generated script finds the slide closest to the snap position, then calls scrollIntoView on the neighbour with smooth behaviour, and the CSS scroll-snap settles it exactly in place. It switches to instant scrolling when the visitor has asked for reduced motion. If you leave the buttons off, the HTML and CSS are all you need and the JS tab says so.
- Does it work in every browser?
- scroll-snap-type, scroll-snap-align and scroll-snap-stop are supported in all current desktop and mobile browsers. scrollbar-width and the ::-webkit-scrollbar rule used to hide the scrollbar cover Firefox and Chromium/Safari respectively. Check caniuse.com if you must support very old browsers. The preview on this page is a real scroll-snap container, so what you see is what your browser does.
Related tools
- CSS 3D Tilt Card GeneratorBuild a card that tilts in 3D on hover with perspective and rotateX/rotateY, tune the angle and glare, and copy the CSS.
- CSS View Transitions GeneratorBuild View Transitions animations (crossfade, slide, cover, zoom, blur, wipe, circle reveal) with a live preview, and copy the CSS, HTML and startViewTransition script.
- CSS Button Hover Effects GeneratorBrowse a gallery of button hover effects — sweep, fill, shine, ripple, lift, and outline grow — and copy the CSS.
- CSS Holographic Foil Card GeneratorBuild a holographic trading-card foil effect with a rainbow, gold or silver sheen and cursor-following glare, then copy the CSS, HTML and script.
- CSS Link Underline Effects GeneratorBrowse a gallery of 10 hover underline effects for links — slide in, center out, double line, dot grow, and more.
- CSS Flip Card GeneratorBuild a two-sided card that flips to reveal its back, with horizontal/vertical direction and a hover or click trigger, and copy the CSS.
- CSS Squircle GeneratorBuild an iOS-style continuous-curve squircle from a superellipse formula rendered as a clip-path polygon, and copy the CSS.
- CSS Image Hover Overlay GeneratorBrowse zoom, dim-with-caption, slide-up caption, and icon-reveal hover overlays for images, and copy the CSS.
Updated