scroll-margin

Sets the space left in front of the destination element when you jump to it, for example with an in-page link. Replaces the hack of shifting an empty anchor element so headings don't end up hidden under a fixed header.

Modern approach

Set the space you want in front of it on the destination element. The id goes right on the destination element, so you need neither an empty element nor a wrapper.

h2 {
  scroll-margin-top: 80px;
}
<h2 id="contact">Contact</h2>

Legacy approach

The heading was wrapped in a container, and an empty anchor element was absolutely positioned and shifted up by the header height. Since the id goes on the anchor, the heading’s layout stays the same, but you need an extra element and a wrapper just for the id.

.section {
  position: relative;
}

.anchor {
  position: absolute;
  top: -80px;
}
<section class="section">
  <a id="contact" class="anchor"></a>
  <h2>Contact</h2>
</section>

Things to watch when replacing

  • It works when the scroll targets an element: in-page links, opening a URL with a #, and scrollIntoView(). If you use scroll snapping, it also affects where scrolling stops. Otherwise it has no effect on scrollTo(), which takes coordinates.
  • You set it on each destination element. To define the header height in one place, you can set scroll-padding-top on html instead. scroll-padding-top has also worked in all browsers since April 2021.
  • If the header height changes with the viewport width, put the height in a custom property and use it for both the header and scroll-margin-top.

Browser Support

  • Chrome

    v69+
    2018-09-04
  • Edge

    v79+
    2020-01-15
  • Safari

    v14.1+
    2021-04-26
    Fallback
    from v11
    scroll-snap-margin
  • Safari (iOS)

    v14.5+
    2021-04-26
    Fallback
    from v11
    scroll-snap-margin
  • Firefox

    v90+
    2021-07-13

Search by feature name or code

e.g. aspect-ratio, :has(), padding-top

Try a different word or a property name.

Could not load search.