CSS Scroll Snap
Scroll snap lets a scroll container rest at predictable positions after a user scrolls or swipes. You can build horizontal card strips, image carousels, and full-height section paging without JavaScript timers or wheel hijacking, using only CSS on the scroll container and its children.
This learning activity explains the core properties, the difference between mandatory and proximity snapping, and how to combine snap with accessible scrolling habits. For a concise overview from the Chrome team, see Well-controlled scrolling with CSS Scroll Snap (web.dev).
How Scroll Snap Works
You declare two roles:
-
Scroll container (often with
overflow: autoorscroll): setscroll-snap-typeto define the axis and strictness. -
Snap targets (children): set
scroll-snap-align(and optionallyscroll-snap-stop,scroll-margin) so the browser knows where to align each item when scrolling stops.
.carousel {
display: flex;
gap: 1rem;
overflow-x: auto;
scroll-snap-type: x mandatory;
scroll-padding-inline: 1rem;
}
.carousel__slide {
flex: 0 0 80%;
scroll-snap-align: start;
scroll-snap-stop: always;
}
scroll-snap-type
Applied to the scroll container. Syntax (simplified):
scroll-snap-type: none;
scroll-snap-type: x | y | block | inline | both;
/* Often combined with mandatory | proximity */
scroll-snap-type: x mandatory;
scroll-snap-type: y proximity;
-
none: disables snap (default). -
x/y: horizontal or vertical axis in physical coordinates;inline/blockfollow writing mode. -
mandatory: the scroll position must settle on a snap point (stronger). -
proximity: snap only if the scroll ends near a snap point (softer, often feels more natural for long pages).
The element must actually scroll: set explicit dimensions or constrain content so overflow creates a scrollport. Flex and grid children often need min-width: 0 (or min-height) so they can shrink and allow overflow.
scroll-snap-align
Applied to snap children. Common values: start, center, end. The browser aligns the snap area of each child to the snapport of the container when scrolling stops.
scroll-snap-stop
Lets you force the user to stop at each item on the way (useful for full-bleed slides):
.slide {
scroll-snap-align: center;
scroll-snap-stop: always;
}
Without this, fast flings may skip across multiple snap points.
scroll-padding and scroll-margin
scroll-padding-* on the container insets the snapport (for example, to keep slides clear of a fixed header or edge gutters).
scroll-margin-* on children adjusts where each item’s snap position sits, similar to how margin affects layout but for snapping.
Demo: Horizontal Card Strip
Drag the scrollbar or swipe on a trackpad. Each card snaps to the start of the scrollport.
.track {
display: flex;
gap: 0.75rem;
overflow-x: auto;
scroll-snap-type: x mandatory;
}
.card {
flex: 0 0 70%;
scroll-snap-align: start;
}
Demo: Vertical Section Snapping
Full-height sections with scroll-snap-type: y mandatory create a “page at a time” feel. Use sparingly on long-form reading pages; it fits marketing landings and slideshow-style layouts.
Accessibility and UX
- Keyboard: Scrollable regions must be focusable or contain focusable elements so keyboard users can reach them. A horizontal carousel often needs arrow buttons or visible focus management in addition to raw scrolling.
- Do not trap scroll: Avoid nested snap containers that fight each other or block the main page scroll without an alternative.
-
Reduced motion: Scroll snap is not the same as animation, but aggressive
mandatorysnapping on long content can feel disruptive. Some teams loosen toproximityinside@media (prefers-reduced-motion: reduce); test with real users. - Carousels: If you build a carousel pattern, follow WAI guidance: provide previous/next controls, expose position in the set, and do not auto-advance in a way that blocks reading.
Scroll snap is a layout aid, not a replacement for accessible carousel semantics. Complex widgets may still need appropriate ARIA roles and keyboard interaction patterns.
Debugging Tips
- In Chromium DevTools, the Layout pane can help visualize scroll containers.
- If snap “does nothing,” verify overflow, axis choice, and that children are direct participants in the scroll (not clipped unexpectedly by a parent).
Summary
Use scroll-snap-type on the scroller and scroll-snap-align on items; tune with scroll-padding, scroll-margin, and scroll-snap-stop. Prefer proximity when rigid snapping feels heavy, and always consider keyboard users and motion preferences when snap affects navigation.