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.