Home / Snippets / Layout /

Scroll reset

Three scroll properties worth setting globally: smooth anchoring, a scroll-margin-top offset for fixed headers, and overscroll-behavior: none to prevent the browser bounce on locked UI.

scroll-behavior: smoothanimated jump-to-anchor navigation
scroll-margin-top: 5remoffset anchors below fixed header height
overscroll-behavior: noneno bounce / pull-to-refresh on scroll lock
Widely Supported
layoutresetno-js

Quick implementation

/* Smooth scrolling — respects prefers-reduced-motion */
@media (prefers-reduced-motion: no-preference) {
  html {
    scroll-behavior: smooth;
  }
}

/* Offset anchor scroll destination below a fixed header */
:target {
  scroll-margin-top: 5rem; /* match your header height */
}

/* Apply to all heading anchors on docs/blog sites */
[id] {
  scroll-margin-top: 5rem;
}

/* Prevent pull-to-refresh and overscroll bounce on modal/drawer */
.modal,
.drawer,
[data-scroll-lock] {
  overscroll-behavior: contain;
}

/* Full app shell — no browser bounce at all */
body {
  overscroll-behavior: none;
}

Prompt this to your LLM

Includes role, constraints, framework variants, and edge cases.

You are a senior frontend engineer writing a CSS scroll reset
for a content site with a fixed 64px navbar.

Goal: Set sensible global scroll defaults using only CSS.

Include:
- smooth scroll-behavior wrapped in prefers-reduced-motion
  (never apply smooth scroll unconditionally — it can cause motion
  sickness for users with vestibular disorders)
- scroll-margin-top: 5rem on [id] and :target to offset anchors
  below the fixed header — without this, heading links are hidden
  behind the navbar
- overscroll-behavior: contain on modals/drawers to prevent
  background scroll chain
- overscroll-behavior: none on body only for app shells (not content
  sites — it breaks browser swipe-back gesture on mobile)

Explain the difference between overscroll-behavior: none vs contain.

Framework variant: Show how to implement this in a Next.js app where
the fixed header height is controlled by a CSS custom property
--header-h: 4rem, and how to update that property on resize with
a ResizeObserver.

Return only the CSS with inline comments.

scroll-margin-top and fixed headers

When a page has a fixed header and a user clicks an in-page anchor link (<a href="#section">), the browser scrolls the target element to the top of the viewport — directly under the fixed header. The content is technically at the right position in the DOM, but visually it's hidden. scroll-margin-top: 5rem adds a gap above the element when it's scrolled into view, pushing it below the header. The value should match your header's height. Setting it on [id] covers every heading with an anchor, including those generated by markdown processors like remark.

overscroll-behavior: contain vs none

overscroll-behavior: contain prevents the scroll event from propagating to the parent when the child scrollable area hits its boundary — useful for modals and drawers where you don't want background page scrolling. overscroll-behavior: none also prevents the pull-to-refresh gesture and browser navigation swipes. Use none sparingly — removing swipe-back navigation is a significant UX degradation on mobile. Reserve it for full-screen app shells where those gestures don't make sense.