CSS Scroll Snap and Smooth Scrolling
CSS can control how a page scrolls: it can glide smoothly to a section, and it can stop neatly on each item of a carousel.
Smooth Scrolling
When a link points to a part of the same page, like href="#contact", the browser normally jumps there instantly. One line makes it glide instead:
Example
html {
scroll-behavior: smooth;
}| Value | Effect |
|---|---|
auto | Jumps straight to the place (the default) |
smooth | Scrolls smoothly to the place |
Fixing the Sticky Header Problem
If your page has a sticky header, a jump link puts the section heading under the header, where nobody can see it. scroll-margin-top leaves room above the target.
Example
<!DOCTYPE html>
<html>
<head>
<style>
html { scroll-behavior: smooth; }
body { margin: 0; font-family: Arial, sans-serif; }
header {
position: sticky;
top: 0;
background-color: #264de4;
padding: 12px;
}
header a { color: white; margin-right: 12px; }
section {
min-height: 500px;
padding: 16px;
scroll-margin-top: 50px;
}
#about { background-color: #eef2ff; }
#courses { background-color: #fef3c7; }
#contact { background-color: #dcfce7; }
</style>
</head>
<body>
<header>
<a href="#about">About</a>
<a href="#courses">Courses</a>
<a href="#contact">Contact</a>
</header>
<section id="about"><h2>About Us</h2></section>
<section id="courses"><h2>Our Courses</h2></section>
<section id="contact"><h2>Contact</h2><p>Orangi Town, Karachi</p></section>
</body>
</html>Try it Yourself »Click the links. The page glides, and each heading stops just below the blue header.
Smooth scrolling can bother people who get dizzy from motion. Turn it on only for users who have not asked for less motion: put the rule inside @media (prefers-reduced-motion: no-preference) { ... }. See the CSS Accessibility chapter.
Scroll Snap
Scroll snap makes a scroll area stop exactly on an item, like swiping photos on a phone. You need two properties:
- On the scroll container:
scroll-snap-type - On each child:
scroll-snap-align
| Property | Common values | Meaning |
|---|---|---|
scroll-snap-type | x mandatory, y proximity | Which direction snaps, and how strongly |
scroll-snap-align | start, center, end | Which part of the child lines up with the container |
scroll-snap-stop | always | Do not skip past this item on a fast swipe |
scroll-padding | 16px | Space kept inside the container's edge when snapping |
mandatoryalways snaps to an item when scrolling stops.proximityonly snaps if you stop close to an item.
A Swipeable Carousel
Example
<!DOCTYPE html>
<html>
<head>
<meta name="viewport" content="width=device-width, initial-scale=1">
<style>
body { font-family: Arial, sans-serif; }
.carousel {
display: flex;
gap: 12px;
overflow-x: auto;
scroll-snap-type: x mandatory;
scroll-padding: 0 16px;
padding: 0 16px 12px;
}
.slide {
flex: 0 0 80%;
scroll-snap-align: start;
aspect-ratio: 16 / 9;
border-radius: 10px;
display: grid;
place-items: center;
color: white;
font-size: 1.5rem;
}
.slide:nth-child(1) { background-color: #264de4; }
.slide:nth-child(2) { background-color: crimson; }
.slide:nth-child(3) { background-color: seagreen; }
.slide:nth-child(4) { background-color: #f97316; }
</style>
</head>
<body>
<h2>Student Projects</h2>
<div class="carousel">
<div class="slide">Restaurant website</div>
<div class="slide">Online shop</div>
<div class="slide">Portfolio</div>
<div class="slide">School app</div>
</div>
</body>
</html>Try it Yourself »Scroll the row sideways (or swipe on a phone). It always stops with a slide lined up at the start. Each slide is 80% wide, so a part of the next slide peeks out, which tells users there is more to see.
Full-Page Vertical Sections
The same idea works up and down. Each section fills the screen, and the page snaps from one to the next.
Example
.page {
height: 100dvh;
overflow-y: auto;
scroll-snap-type: y mandatory;
}
.page section {
height: 100dvh;
scroll-snap-align: start;
}Use vertical mandatory snapping with care. If a section is taller than the screen, users may not be able to read its bottom part. proximity is safer for long content.
Hiding the Scroll Bar
Carousels often look cleaner without a scroll bar. Users can still swipe or use the arrow keys.
Example
.carousel {
scrollbar-width: none;
}Use scrollbar-width: thin if you want a smaller bar instead of none, and scrollbar-color to colour it.
Exercise
Exercise
Which pair of properties creates scroll snapping?
