Scroll Snap
CSS Scroll Snap lets a scrollable container automatically stop — "snap" — at specific points as the user scrolls, instead of coming to rest wherever momentum happens to run out. This is the mechanism behind image carousels, full-page sections, and swipeable card decks that always land cleanly on an item, and it does all of it with two properties: no JavaScript, no scroll-event listeners, no manual position calculations.
scroll-snap-type: setting up the container
scroll-snap-type goes on the scrollable container and takes an axis (x, y, or both) plus a strictness (mandatory always snaps, while proximity only snaps if the container comes to rest near a snap point):.carousel {
display: flex;
overflow-x: auto;
scroll-snap-type: x mandatory;
}scroll-snap-align: marking the snap points
scroll-snap-align goes on the children — it marks which edge of each child should align with the container when it snaps (start, center, or end):.carousel__item {
flex: 0 0 100%;
scroll-snap-align: center;
}Worked example: an image carousel
Combining the two properties on a horizontally scrolling flex container produces a gallery where each image snaps fully into view:
.gallery {
display: flex;
gap: 1rem;
overflow-x: auto;
scroll-snap-type: x mandatory;
scroll-padding: 1rem; /* keeps items clear of the container edge */
}
.gallery__image {
flex: 0 0 80%;
scroll-snap-align: center;
border-radius: 8px;
}<div class="gallery"> <img class="gallery__image" src="photo-1.jpg" alt="" /> <img class="gallery__image" src="photo-2.jpg" alt="" /> <img class="gallery__image" src="photo-3.jpg" alt="" /> </div>
The user can scroll or swipe through this container with a mouse wheel, trackpad, or touchscreen, and it always comes to rest with one image centered — no carousel library required.
scroll-snap-stop: preventing skipped snap points
scroll-snap-stop: always forces the browser to stop at every snap point it passes, one at a time, regardless of scroll speed:.gallery__image {
scroll-snap-align: center;
scroll-snap-stop: always; /* can't be flung past — one item per gesture */
}Property | Applied to | Purpose |
|---|---|---|
scroll-snap-type | Scroll container | Enables snapping and its axis/strictness |
scroll-snap-align | Snap targets (children) | Marks which edge aligns on snap |
scroll-snap-stop | Snap targets (children) | Forces stopping at every point, no skipping |
scroll-snap-type: x mandatory forces the browser to always rest on a snap point, which can feel awkward if items are wider than the container or if there's empty trailing space with no snap point to land on. When in doubt, proximity is a gentler default that only snaps when the user scrolls near a snap point in the first place.