CSS scroll-margin Property

Sets the margin area used for snapping an element to the scrollport, providing a buffer zone when navigating to an anchor or using scroll snapping.

selector { scroll-margin: value; }
<length> Specifies a fixed distance from the edge of the scroll container using units like px, em, or rem.
<percentage> Specifies a distance relative to the size of the scroll container.

Code Examples

A basic example showing how to use scroll-margin-top to prevent a fixed header from overlapping section content when navigating via anchor links.

<style>
  header {
    position: fixed;
    top: 0;
    height: 60px;
    width: 100%;
    background: #333333;
    color: #ffffff;
  }
  section {
    height: 100vh;
    border: 1px solid #cccccc;
    /* Offsets the scroll position so content isn't under the 60px header */
    scroll-margin-top: 70px;
  }
</style>

<header>Fixed Nav</header>
<nav>
  <a href="#sec1">Go to Section 1</a>
  <a href="#sec2">Go to Section 2</a>
</nav>
<section id="sec1">Content for Section 1</section>
<section id="sec2">Content for Section 2</section>

An advanced example using scroll snapping and JavaScript to dynamically adjust the scroll-margin buffer when an element is clicked and scrolled into view.

<div id="scroller" style="display: flex; overflow-x: auto; scroll-snap-type: x mandatory; width: 300px; border: 2px solid #000000;">
  <div class="item" style="flex: 0 0 200px; height: 200px; background: #ff0000; scroll-snap-align: start; scroll-margin: 20px;">Item 1</div>
  <div class="item" style="flex: 0 0 200px; height: 200px; background: #00ff00; scroll-snap-align: start; scroll-margin: 20px;">Item 2</div>
  <div class="item" style="flex: 0 0 200px; height: 200px; background: #0000ff; scroll-snap-align: start; scroll-margin: 20px;">Item 3</div>
</div>

<script>
  const items = document.querySelectorAll(".item");
  items.forEach(item => {
    item.addEventListener("click", () => {
      // Dynamically increasing the scroll margin on click
      item.style.scrollMargin = "50px";
      item.scrollIntoView({ behavior: "smooth" });
    });
  });
</script>

Pro Tip

If your site has a dynamic header that changes height on mobile versus desktop, use a CSS variable for your scroll-margin value. This way, you can update one variable in your media queries and all your scroll targets will adjust their landing positions automatically.

Deep Dive

Think of scroll-margin as a safety buffer for your viewport. When a user clicks a link to an ID on your page, the browser normally snaps that element right to the top edge of the screen. If you have a fixed navigation bar, the element gets tucked underneath it and becomes invisible. This property tells the browser, "When you scroll to me, stop this many pixels early." It does not affect the actual layout or spacing of elements on the page like a normal margin does; it only changes the landing zone of the scroll container's eye.

Best Practices

Always apply this property to any section or heading that serves as an anchor link target if your site uses a sticky or fixed header. Use the shorthand scroll-margin to set all sides at once, or use scroll-margin-top specifically for vertical scrolling layouts. It is much cleaner than using the old-school hack of adding hidden padding and negative margins to your elements.

Common Pitfalls

The most frequent mistake is mixing up scroll-margin and scroll-padding. You apply scroll-margin to the child element (the target you are scrolling to), while scroll-padding is applied to the parent container. Also, remember that this property has no effect on the visual flow of the page; it only exists for the scroll container's coordinate system.

Accessibility

This is a win for accessibility. It ensures that when a keyboard user tabs to a link or an automated script moves focus to an element, that element remains fully visible and not obscured by floating UI elements like headers or chat widgets.

Dev Data Table: scroll-margin property

default 0
animatable no
inherited no
experimental no
year_intro 2018
year_standard 2019
js_syntax_1 element.style.scrollMargin = "20px";
js_syntax_2 element.style.setProperty("scroll-margin", "20px");
js_note When manipulating this in JavaScript, use camelCase for the style object property or the kebab-case string within the setProperty method.
browsers { "Chrome": 69, "Edge": 79, "Firefox": 68, "Safari": 14.1, "Opera": 56, "Chrome Android": 69, "Safari on iOS": 14.5, "Samsung Internet": 10.1, "Opera Mobile": 48 }
results render here...