CSS scroll-margin-block Property

Sets the scroll margin offsets for the start and end sides of an element in the block dimension.

selector { scroll-margin-block: <length> | <length> <length>; }
<length> Defines a fixed distance for the scroll margin using standard units like px, rem, or vh.

Code Examples

A basic example showing how to prevent a sticky header from overlapping an anchor-linked section.

<style>
  section {
    scroll-margin-block: 80px;
    height: 600px;
    background: #e0e0e0;
    border-bottom: 2px solid #333333;
  }
  nav {
    position: fixed;
    top: 0;
    height: 60px;
    width: 100%;
    background: #ffcc00;
  }
</style>
<nav>Sticky Header (60px tall)</nav>
<a href="#target">Jump to Section 2</a>
<section>Section 1</section>
<section id="target">Section 2 (Stops 80px from top)</section>

An advanced implementation using CSS scroll snapping and JavaScript to dynamically update the scroll margin.

<style>
  .scroll-box {
    height: 300px;
    overflow-y: scroll;
    scroll-snap-type: y mandatory;
    border: 2px solid #000000;
  }
  .item {
    height: 200px;
    scroll-snap-align: start;
    scroll-margin-block: 20px 0;
    background: #3498db;
    margin: 10px 0;
    display: flex;
    align-items: center;
    justify-content: center;
    color: #ffffff;
  }
</style>
<div class="scroll-box" id="viewer">
  <div class="item">Item 1</div>
  <div class="item">Item 2</div>
  <div class="item">Item 3</div>
</div>
<button onclick="updateScrollGap()">Increase Gap</button>
<script>
  function updateScrollGap() {
    const items = document.querySelectorAll('.item');
    items.forEach(item => {
      item.style.scrollMarginBlock = '60px 0px';
    });
    console.log('Scroll margin updated for snapping.');
  }
</script>

Pro Tip

You can provide two different values to this shorthand to handle the start and end differently. For example, if you have a sticky header at the top and a sticky chat widget or toolbar at the bottom, use 'scroll-margin-block: 100px 50px;' to protect your content from being obscured on both ends during a scroll snap.

Deep Dive

This property is a shorthand for scroll-margin-block-start and scroll-margin-block-end. Think of it as a way to define a safety zone or a landing pad for your elements. When a user clicks a link to an anchor on your page, the browser usually slams that element right against the edge of the viewport. By using this property, you are telling the browser to stop the scroll a certain distance away from the element. Because it is a logical property, it respects the writing-mode. In standard horizontal layouts, it affects the top and bottom. In vertical layouts, it affects the left and right. It ensures that when an element is snapped into view or jumped to via URL hash, it sits exactly where you want it relative to the scroll container's edge.

Best Practices

Use this property primarily to handle fixed headers. Instead of using hacky solutions like hidden elements with negative margins, apply scroll-margin-block to your actual content sections. This creates the necessary offset so your fixed navigation menu doesn't cover up the heading of the section the user just navigated to. It is much cleaner and doesn't interfere with the actual document flow or layout of the elements on the screen.

Common Pitfalls

A common mistake is applying this property to the parent scroll container instead of the child element that is being scrolled into view. This property must be set on the target element itself. Also, remember that it only takes effect during scroll operations, such as clicking an anchor link or utilizing CSS scroll snapping. It does not act like a standard margin that pushes other elements away in the document flow.

Accessibility

This property is a huge win for accessibility. When keyboard users navigate through a page using a table of contents or skip links, they need to see exactly where they have landed. Without scroll-margin-block, the target content can be hidden behind sticky headers or clipped by the browser UI, causing confusion. Proper use ensures the focused element is fully visible and has context around it.

Dev Data Table: scroll-margin-block property

default 0
animatable no
inherited no
experimental no
year_intro 2018
year_standard 2021
js_syntax_1 element.style.scrollMarginBlock = '50px 20px';
js_syntax_2 element.style.setProperty('scroll-margin-block', '50px');
js_note In JavaScript, the property name uses camelCase. If setting two values, they must be passed as a single string.
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...