CSS Scroll Behavior

CSS controls how, where, and how fast a page scrolls. From smooth page navigation to snapping carousels to hiding scrollbars, scroll-related CSS properties create a polished, intentional reading experience without any JavaScript.

scroll-behavior — Smooth vs Instant

By default, clicking an anchor link (<a href="#section">) jumps instantly to the target. The scroll-behavior property makes this scroll smooth and animated instead.

html {
  scroll-behavior: smooth;
}
DIAGRAM — scroll-behavior:

scroll-behavior: auto (default)
Click "About" link → page JUMPS instantly to the About section

scroll-behavior: smooth
Click "About" link → page GLIDES down to the About section

Two values are available:

  • auto — instant jump (default)
  • smooth — animated glide

Accessibility Note

Some users experience motion sickness from scrolling animations. Respect the operating system's reduced-motion preference.

html {
  scroll-behavior: smooth;
}

@media (prefers-reduced-motion: reduce) {
  html {
    scroll-behavior: auto;
  }
}

scroll-margin and scroll-padding — Offset the Scroll Target

When you have a sticky header, smooth-scrolling to an anchor lands the heading behind the header. scroll-padding and scroll-margin fix this.

scroll-padding-top (on the scroll container)

html {
  scroll-behavior: smooth;
  scroll-padding-top: 80px; /* matches your sticky header height */
}
DIAGRAM — Without vs with scroll-padding-top:

Without:
  ┌────────────────────┐
  │Sticky Header(80px) │  ← header
  │████████████████████│
  │ ## Section Title   │  ← hidden behind header!
  │ content...         │

With scroll-padding-top: 80px:
  ┌────────────────────┐
  │Sticky Header(80px) │  ← header
  │────────────────────│
  │ ## Section Title   │  ← visible below header
  │ content...         │

scroll-margin-top (on the target element)

section {
  scroll-margin-top: 80px;
  /* Add gap above this element when it's scrolled to */
}

Both approaches achieve the same visual result. Use scroll-padding-top on html as a global solution, or scroll-margin-top on individual elements when you need per-element control.

overflow — Controlling Scrollable Areas

The overflow property controls what happens when content is larger than its container. It also determines whether a scrollbar appears.

DIAGRAM — overflow values:

Content bigger than box:

overflow: visible  → content spills outside the box (default)
overflow: hidden   → content is clipped, no scrollbar
overflow: scroll   → always shows scrollbar (even if not needed)
overflow: auto     → shows scrollbar only when content overflows
/* Scrollable code block */
pre {
  overflow-x: auto;  /* horizontal scroll only */
  white-space: nowrap;
}

/* Scrollable sidebar */
.sidebar {
  height: 100vh;
  overflow-y: auto;  /* vertical scroll only */
}

/* No scrollbar, but content is clipped */
.card {
  overflow: hidden;
  border-radius: 8px;
}

CSS Scroll Snap — Controlled Snapping

Scroll snap makes a scrollable container lock onto defined positions as the user scrolls — like a slideshow or a mobile page-by-page view.

On the Container (the scroll parent)

.carousel {
  display: flex;
  overflow-x: scroll;
  scroll-snap-type: x mandatory;
  /* x = horizontal, mandatory = always snap to a position */
}

On Each Snap Target (the children)

.slide {
  flex: 0 0 100%;  /* each slide is full width */
  scroll-snap-align: start;
}
DIAGRAM — scroll-snap-type: x mandatory:

Scrolling freely:    →→→
  ┌──┬──┬──┬──┬──┐
  │1 │2 │3 │4 │5 │   ← slides in a row

After releasing:     SNAP
  ┌──────┐
  │  3   │           ← snaps to the nearest full slide
  └──────┘

No half-visible slides — every stop is a clean frame.

Snap Alignment Options

  • scroll-snap-align: start — snaps the start (left/top) edge of the item to the container edge
  • scroll-snap-align: center — snaps the item's center to the container's center
  • scroll-snap-align: end — snaps the end (right/bottom) edge

Proximity vs Mandatory

/* mandatory — always snaps, even on small scroll */
scroll-snap-type: x mandatory;

/* proximity — only snaps if close enough to a snap point */
scroll-snap-type: x proximity;

Vertical Page Sections

.page-wrapper {
  height: 100vh;
  overflow-y: scroll;
  scroll-snap-type: y mandatory;
}

section {
  height: 100vh;
  scroll-snap-align: start;
}
DIAGRAM — Full-screen vertical snap sections:

┌─────────────────┐  ─── scroll starts here
│   Section 1     │    ↑ snaps here
│  (100vh tall)   │
├─────────────────┤    ↑ snaps here
│   Section 2     │
│  (100vh tall)   │
├─────────────────┤    ↑ snaps here
│   Section 3     │
└─────────────────┘

Hiding the Scrollbar While Keeping Scroll

Useful for carousels and horizontal scroll areas where the scrollbar looks out of place.

.scroll-container {
  overflow-x: auto;
  scrollbar-width: none;     /* Firefox */
  -ms-overflow-style: none;  /* IE/Edge */
}

/* Chrome, Safari */
.scroll-container::-webkit-scrollbar {
  display: none;
}

Custom Scrollbar Styling

/* Works in Chrome, Edge, Safari */
::-webkit-scrollbar {
  width: 8px;
}

::-webkit-scrollbar-track {
  background: #f1f1f1;
}

::-webkit-scrollbar-thumb {
  background: #aaa;
  border-radius: 4px;
}

::-webkit-scrollbar-thumb:hover {
  background: #888;
}

Quick Reference

PropertyWhat It Does
scroll-behavior: smoothAnimates anchor-link scrolling
scroll-padding-topAdds top offset for sticky headers
scroll-margin-topPer-element scroll target offset
overflow-y: autoVertical scrollbar when needed
overflow-x: autoHorizontal scrollbar when needed
scroll-snap-typeEnable snapping on a container
scroll-snap-alignDefine snap position on children
scrollbar-width: noneHide scrollbar (Firefox)

Scroll behavior is one of the most noticeable parts of a user's experience. Smooth anchors, properly offset headings, and snapping carousels feel polished and intentional — and all of it is achievable with a few CSS properties and no JavaScript.

Leave a Comment

Your email address will not be published. Required fields are marked *