← Back to CSS Course | Chapter 8: Animations & Effects | Lesson 20 of 22

CSS Scroll Snap

Think of a slideshow or a photo gallery where scrolling gently clicks each image into a neat, centered position, instead of leaving you stopped halfway between two photos. CSS Scroll Snap makes that satisfying, controlled scrolling behavior possible with pure CSS, no JavaScript required. You mark a scrollable container as a snap container with scroll-snap-type, then mark its children as snap targets with scroll-snap-align. As a visitor scrolls, the browser automatically settles on the nearest snap point, the same way a native app's carousel feels. This is a great fit for a horizontally scrolling tool gallery or feature carousel on cookiescursor.com, giving it a smooth, native-feeling scroll experience.

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?

css
<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

css
<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

css
<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

css
<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

css
<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>
Common Mistakes
  1. Setting scroll-snap-type on the container but forgetting scroll-snap-align on its children, resulting in no visible snapping at all.
  2. Using mandatory snapping on a container with only one or two children, which can make scrolling feel oddly restrictive.
  3. Forgetting overflow: auto (or scroll) on the snap container, without it there's nothing to actually scroll and snap within.
Chapter Summary
  • 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.
Browser Support

CSS Scroll Snap properties are supported by all modern browsers.

Login to run this code

C/C++/Java/PHP execution requires a free account. Your code is saved — you'll land right back in the editor after logging in.