Scroll Snap
Makes a scroll container come to rest on the items inside it rather than wherever the gesture left it. Check out the MDN for how to use.
Two properties do it together: scroll-snap-type on the scroll container says which axes snap, and scroll-snap-align on the items in it says where each one lines up. Three more adjust the result — scroll-padding on the container, scroll-margin and scroll-snap-stop on the items.
.strip { flex-direction: row; overflow-y: hidden; height: 160px; padding: 16px; border-radius: 12px; background-color: #f1f5f9; scroll-snap-type: x mandatory; } .card { width: 140px; height: 120px; margin-right: 12px; flex-shrink: 0; align-items: center; justify-content: center; border-radius: 16px; background-image: linear-gradient(160deg, #6366f1, #0ea5e9); color: white; font-size: 28px; scroll-snap-align: center; } .controls { flex-direction: row; gap: 8px; margin-bottom: 12px; }
The snap is taken once the scroll settles, so nothing fights the gesture while it is happening — and it is measured from where a fling would have landed rather than from where the pointer let go, so a flick chooses the item it was thrown at.
scroll-snap-type
An axis, and optionally how strictly it snaps.
none(default) — nothing snaps.x,y,both— the axes to snap on.block,inline— the same two axes by their logical names. There are no writing modes here, soblockis alwaysyandinlineis alwaysx.proximity(the default when no strictness is given) — snap only when the scroll comes to rest near a snap point.mandatory— always rest on a snap point, however far away the nearest one is.
An axis is required: scroll-snap-type: mandatory names nothing to snap and is not a value.
scroll-snap-align
Where an item lines up inside the scrollport, one alignment per axis.
none(default) — the item is not a snap point.start— the item’s start edge meets the scrollport’s start edge.end— the item’s end edge meets the scrollport’s end edge.center— the item is centred in the scrollport.
One value covers both axes. Two are block first, as every logical pair is, so scroll-snap-align: start center starts vertically and centres horizontally.
Any descendant of the scroll container can be a snap point, not only a direct child — except one inside a nested scroll container, which belongs to that one instead.
scroll-padding
Insets the snapport — the box a snap target is lined up against — inside the scroll container’s own scrollport. A header pinned over the top edge is the usual reason to want one: without it a start-aligned item snaps under the header rather than below it.
Set on the scroll container, as a length or a percentage of the scrollport on that axis. auto is read as none of it. The four physical edges, the two-value scroll-padding-inline and scroll-padding-block, their four logical longhands and the four-way scroll-padding shorthand all work.
#list {
scroll-snap-type: y mandatory;
scroll-padding-top: 48px;
}
scroll-margin
Outsets a target’s snap area, which is its border box otherwise — so an item can ask for a gap between itself and the snapport edge it meets.
Set on the snap target, as a length. Percentages are not a value: the box being outset is the only thing they could resolve against. The same set of edges and shorthands as scroll-padding.
scroll-snap-stop
Whether a gesture may carry past this target on its way to one further along.
normal(default) — a fling lands wherever it was aimed.always— the first such target the travel goes over is where the scroll has to come to rest.
always holds whatever the strictness, so it stops a proximity container that would otherwise not have snapped at all. It applies to gestures — a wheel, a drag — and not to an asked-for scroll: setting scrollTop or calling scrollTo lands where it was sent.
Notes
- The scrollport is measured to the container’s padding edge, so a
startsnap on the first item is the top of the travel rather than an offset that scrolls the padding away. - An item too far along to be reached snaps to the closest offset that shows it, which is how a
start-aligned last item still counts as snapped at the end of the travel. - How near
proximityhas to be is left to the implementation by CSS; here it is half the scrollport. - A snap animates over the same duration a wheel scroll smooths over, or over
scroll-behavior’s when that asks forsmooth. A re-snap after a resize is instant, as CSS specifies. - The three properties above are read only by snapping. There is no
scrollIntoViewhere and no scroll anchoring, which is everything else CSS uses them for.