Fixed Headers, On-Page Links, and Overlapping Content, Oh My!

Avatar of Chris Coyier
Chris Coyier on (Updated on )

Let’s take a basic on-page link:

<a href="#section-two">Section Two</a>

When clicked, the browser will scroll itself to the element with that ID: <a href="#section-two">Section Two</a>. A browser feature as old as browsers themselves, just about.

But as soon as position: fixed; came into play, it became a bit of an issue. The browser will still jump to bring the newly targeted element into view, but that element may be obscured by a fixed position element, which is pretty bad UX.

I called this “headbutting the browser window” nearly 10 years ago, and went over some possible solutions. Nicolas Gallager documented five different techniques. I’m even using a fixed position header here in v17 of CSS-Tricks, and I don’t particularly love any of those techniques. I sort of punted on it and added top padding to all my <h3> elements, which is big enough for the header to fit there.

There is a new way though! Finally!

Šime Vidas documented this in Web Platform News. There are a bunch of CSS properties that go together as part of CSS scroll snapping, but it turns out that scroll-padding and scroll-margin can be used outside of a scroll snapping container.

html {
  scroll-padding-top: 70px; /* height of sticky header */
}

When this was first published, it was Chrome-only, but as I update this in April 2021, it works in Firefox and Safari as well!

Hiroyuki Ikezoe wrote in to tell me that <body> is not the correct place to use scroll-padding, as document.scrollingElement is actually <html>. Unfortunately, Chrome has it implemented (v73) such that it only works on the <body> right now, but there is a bug filed and the likely outcome is that it stops working on the <body> and only works on <html>. This is the same situation as the native specced custom scrollbars: they only work on <html>.