CSS Scroll Snap
In this page:
What Is Scroll Snap?
Scroll Snap lets a scrollable container automatically settle on predefined positions as a visitor scrolls, creating the smooth, clicking-into-place feel of a native carousel or slideshow, using only CSS.
Note: Scroll Snap works great for horizontally scrolling tool cards or image galleries, where each item deserves its own clean, centered stop.
Warning: Scroll Snap changes how scrolling settles, but it doesn't create the scrollable container itself, you still need overflow and a defined size.
Example: What Is Scroll Snap?
<style>
.container {
overflow-x: scroll;
scroll-snap-type: x mandatory;
display: flex;
gap: 10px;
}
.item {
scroll-snap-align: center;
display: inline-block;
width: 200px;
height: 80px;
background: lightblue;
flex-shrink: 0;
}
.item:nth-child(2) {
background: lightgreen;
}
</style>
<div class="container">
<div class="item">1</div>
<div class="item">2</div>
</div>
scroll-snap-type
scroll-snap-type is set on the scrollable container itself, defining which axis (x, y, or both) participates in snapping and whether snapping is mandatory or proximity.
Note: Use scroll-snap-type: x mandatory for a horizontal carousel, and scroll-snap-type: y mandatory for a full-screen vertical section scroller.
Warning: scroll-snap-type only sets up the snapping behavior, the children still need scroll-snap-align to actually participate.
Example: scroll-snap-type
<style>
.container {
display: flex;
gap: 10px;
overflow-x: scroll;
scroll-snap-type: x mandatory;
background: #f0f0f0;
border: 1px solid #ccc;
padding: 10px;
}
.item {
scroll-snap-align: start;
flex-shrink: 0;
width: 200px;
height: 80px;
background: lightblue;
}
.item:nth-child(2) { background: lightgreen; }
.item:nth-child(3) { background: lightyellow; }
</style>
<div class="container">
<div class="item">1</div>
<div class="item">2</div>
<div class="item">3</div>
</div>
scroll-snap-align
scroll-snap-align is set on the children of a snap container, marking exactly where each one should align when the browser settles on it, most commonly start, center, or end.
Note: center is a natural choice for a carousel where each item should feel like the visual focus once scrolling stops.
Warning: Without scroll-snap-align on the children, scroll-snap-type on the parent has no children to actually snap to.
Example: scroll-snap-align
<style>
.container {
display: flex;
gap: 10px;
overflow-x: scroll;
scroll-snap-type: x mandatory;
background: #f0f0f0;
border: 1px solid #ccc;
padding: 10px;
}
.item {
scroll-snap-align: center;
flex-shrink: 0;
width: 200px;
height: 80px;
background: lightblue;
}
.item:nth-child(2) { background: lightgreen; }
</style>
<div class="container">
<div class="item">Marks where this child aligns when scrolling stops</div>
<div class="item">2</div>
</div>
Mandatory vs Proximity Snapping
Mandatory snapping always comes to rest on a snap point after every scroll, no exceptions. Proximity snapping only snaps when the scroll happens to stop near a snap point, allowing free scrolling to rest anywhere else.
Note: Use proximity for long, free-scrolling content where snapping should feel like a gentle nudge, not a hard rule.
Warning: Mandatory snapping on a very tall or wide container can feel jarring if a visitor tries to stop midway through casual scrolling.
Example: Mandatory vs Proximity Snapping
<style>
.row {
display: flex;
gap: 10px;
overflow-x: scroll;
padding: 10px;
margin-bottom: 12px;
background: #f0f0f0;
border: 1px solid #ccc;
}
.mandatory { scroll-snap-type: x mandatory; }
.proximity { scroll-snap-type: x proximity; }
.item {
scroll-snap-align: center;
flex-shrink: 0;
width: 200px;
height: 80px;
background: lightblue;
}
.item:nth-child(2) { background: lightgreen; }
</style>
<div class="row mandatory">
<div class="item">Always comes to rest on a snap point</div>
<div class="item">2</div>
</div>
<div class="row proximity">
<div class="item">Only snaps if it stops near one</div>
<div class="item">2</div>
</div>
scroll-padding and scroll-snap-stop
scroll-padding offsets the snap area, useful when a sticky header would otherwise cover the top of a snapped section. scroll-snap-stop: always forces the browser to stop at every snap point in sequence, instead of skipping past several during a fast scroll or swipe.
Note: Set scroll-padding-top to match a sticky header's height so snapped content never ends up hidden underneath it.
Warning: scroll-snap-stop: always can feel sluggish on a very long list of snap points, since it prevents visitors from quickly skimming past several at once.
Example: scroll-padding and scroll-snap-stop
<style>
.container {
display: flex;
gap: 10px;
overflow-x: scroll;
scroll-snap-type: x mandatory;
scroll-padding-left: 60px;
background: #f0f0f0;
border: 1px solid #ccc;
padding: 10px;
}
.item {
scroll-snap-align: start;
scroll-snap-stop: always;
flex-shrink: 0;
width: 200px;
height: 80px;
background: lightblue;
}
.item:nth-child(2) { background: lightgreen; }
.item:nth-child(3) { background: lightyellow; }
</style>
<div class="container">
<div class="item">Offsets past a sticky header, stops at every item</div>
<div class="item">2</div>
<div class="item">3</div>
</div>
- Setting scroll-snap-type on the container but forgetting scroll-snap-align on its children, resulting in no visible snapping at all.
- Using mandatory snapping on a container with only one or two children, which can make scrolling feel oddly restrictive.
- Forgetting overflow: auto (or scroll) on the snap container, without it there's nothing to actually scroll and snap within.
- scroll-snap-type turns a scrollable container into a snap container, along an x, y, or both axis.
- scroll-snap-align marks where each child should snap to, most commonly start or center.
- Mandatory snapping always settles on a snap point, while proximity snapping only does so when scrolling stops near one.
CSS Scroll Snap properties are supported by all modern browsers.
Chapter Quiz — Complete all 22 topics to unlock
0/22 topics done
Complete these topics first:
- CSS Gradients
- CSS Shadows
- CSS Filters
- CSS Blend Modes
- CSS Transforms
- CSS 2D Transforms
- CSS 3D Transforms
- CSS Transitions
- CSS Animations
- CSS Image Styling
- CSS Image Effects
- CSS Hover Overlays
- CSS Image Modal
- CSS Image Centering
- CSS Image Shapes
- CSS Buttons
- CSS Columns
- CSS Clip Path
- CSS Shapes
- CSS Scroll Snap
- CSS Architecture: BEM
- CSS Custom Properties