Pure
Structure

Offset scroll position with a sticky header

A sticky header is a common feature on websites but when used with anchor links or scrollIntoView content can be hidden behind the header. We can solve this using scroll-padding and scroll-margin.

Scroll Padding

Defines for the optimal viewing region of a scrollable region so that when it is scrolled to the padding is taken into account to offset the sticky header.

html {
  scroll-padding-block-start: 80px;
  scroll-behavior: smooth;
}

This will solve our problem if the header has a fixed height but in a real world situation the header usually changes sizes on different viewports so lets address that. We will observe the header for height changes and set a custom property to use for scroll-padding.

let element = document.querySelector('.header');
let height = 0;

const resizeObserver = new ResizeObserver((entries) => {
  for (const entry of entries) {
    let heightNew = entry.contentBoxSize[0].blockSize;

    if (height !== heightNew) {
      height = entry.contentBoxSize[0].blockSize;

      document.documentElement.style.setProperty(`--header-height`, `${height}px`);
    } 
  }

});

resizeObserver.observe(element);
html {
  scroll-padding-block-start: var(--header-height);
  scroll-behavior: smooth;
}

Scroll Margin

Adding a default scroll-margin to elements with an ID would give items like headings more breathing room. [id] { scroll-margin-block-start: 1ex; } However you may want to exclude elements that already have padding around them like a section.