scroll-snap-type
Enables scroll snapping. sstxm is for horizontal carousels, and sstym is for vertical page snapping.
Class List
| Class | CSS | Description |
|---|---|---|
sstn | scroll-snap-type: none | Disable scroll snapping |
sstxm | scroll-snap-type: x mandatory | Horizontal mandatory snap |
sstym | scroll-snap-type: y mandatory | Vertical mandatory snap |
sstbm | scroll-snap-type: both mandatory | Both-axis mandatory snap |
sstxp | scroll-snap-type: x proximity | Horizontal proximity snap |
sstyp | scroll-snap-type: y proximity | Vertical proximity snap |
sstbp | scroll-snap-type: both proximity | Both-axis proximity snap |
Live Demo
Scroll the container below horizontally and let go. Switch the snap type with the buttons and watch where it comes to rest.
sstxm — always pulls to the start of the nearest card once scrolling ends.
Class Details
sstnscroll-snap-type: none
Turns snapping off. Any scroll-snap-align on the children is ignored, which makes it handy with a responsive prefix when a carousel should snap on mobile but scroll freely on desktop.
<!-- Free scrolling: stops wherever the user releases -->
<div class="sstn df oxa gap16px">
<div class="fs0 w28rem h20rem bg18181B br8px"></div>
<div class="fs0 w28rem h20rem bg18181B br8px"></div>
</div>sstxmscroll-snap-type: x mandatory
Mandatory snapping on the horizontal axis. The default choice for card carousels, image galleries, and story viewers where exactly one card should always be framed. The container needs overflow-x and the children need scroll-snap-align.
<!-- Horizontal carousel: container + children both required -->
<div class="sstxm df oxa gap16px">
<div class="ssas fs0 w28rem h20rem bg18181B br8px">Slide 1</div>
<div class="ssas fs0 w28rem h20rem bg18181B br8px">Slide 2</div>
<div class="ssas fs0 w28rem h20rem bg18181B br8px">Slide 3</div>
</div>sstymscroll-snap-type: y mandatory
Mandatory snapping on the vertical axis. Used for full-screen section landing pages and short-form feeds. Scroll the demo below vertically.
<!-- Full-screen vertical section snap -->
<div class="sstym oya h100vh">
<section class="ssas h100vh df jcc aic">Section 1</section>
<section class="ssas h100vh df jcc aic">Section 2</section>
<section class="ssas h100vh df jcc aic">Section 3</section>
</div>Vertical snap demo (scroll inside the container)
sstbmscroll-snap-type: both mandatory
Snaps on both axes. Only meaningful for views that pan in two dimensions, such as map tiles, seating charts, or large data grids. Both axes must actually be scrollable.
<!-- 2D map / spreadsheet style grid snapping -->
<div class="sstbm oa w100p h40rem">
<div class="df">
<div class="ssas fs0 w40rem h40rem bg18181B"></div>
<div class="ssas fs0 w40rem h40rem bg1E1E2E"></div>
</div>
</div>sstxpsstypsstbpproximity
Proximity snapping only aligns when the user stops near a snap point. A good fit when items have uneven heights or long text, where mandatory snapping feels like it is fighting the user.
<!-- Proximity: snaps only when the scroll ends near a snap point -->
<div class="sstxp df oxa gap16px">
<div class="ssas fs0 w28rem h20rem bg18181B br8px"></div>
<div class="ssas fs0 w28rem h20rem bg18181B br8px"></div>
<div class="ssas fs0 w28rem h20rem bg18181B br8px"></div>
</div>mandatory vs proximity
| Type | Behavior |
|---|---|
| mandatory | Always snaps to a snap point when scrolling ends |
| proximity | Snaps only when near a snap point |
Usage Examples
scroll-snap-type is applied to the scroll container, used together with scroll-snap-align on child elements.
<!-- Horizontal carousel -->
<div class="df sstxm oxa">
<div class="fs0 w100p ssas">Slide 1</div>
<div class="fs0 w100p ssas">Slide 2</div>
<div class="fs0 w100p ssas">Slide 3</div>
</div>
<!-- Vertical page snap -->
<div class="sstym oya h100vh">
<section class="h100vh ssas">Section 1</section>
<section class="h100vh ssas">Section 2</section>
</div>Practical example: product carousel
Combining the snap container with scroll padding and overscroll control keeps cards off the viewport edges and stops the swipe from leaking into the page behind.
<!-- Product gallery with a sticky header -->
<header class="ps t0 h6rem bg18181B df aic px2rem">Shop</header>
<!-- spl2rem / spr2rem inset the snap position from the edges,
obxc keeps the swipe from scrolling the page behind -->
<div class="sstxm spl2rem spr2rem obxc swt df oxa gap16px py2rem">
<article class="ssas fs0 w28rem br8px oh bg18181B">
<img src="/p1.jpg" alt="" class="w100p h20rem ofc">
<div class="p16px">
<h3 class="fs16px fw600 cFAFAFA">Product 1</h3>
<p class="fs14px c71717A">$29</p>
</div>
</article>
<article class="ssas fs0 w28rem br8px oh bg18181B">
<img src="/p2.jpg" alt="" class="w100p h20rem ofc">
<div class="p16px">
<h3 class="fs16px fw600 cFAFAFA">Product 2</h3>
<p class="fs14px c71717A">$39</p>
</div>
</article>
</div>Pitfalls
Both the container and the children are required
Setting scroll-snap-type on the container but forgetting scroll-snap-align on the children does nothing at all. This is by far the most common reason snapping appears broken.
There has to be real scrolling
The container needs an overflow such as oxa or oya plus content that actually overflows. Give flex children fs0 so they do not shrink to fit.
mandatory can hurt accessibility
Mandatory snapping can make content between snap points unreachable. If an item may be taller than the viewport, prefer proximity or cap the item height.
Use scroll-padding for edge breathing room
If snapped cards sit flush against the container edge, add scroll padding such as spl2rem on the container, or scroll margin such as sml2rem on individual cards.