The problem: you click a jump link like <a href="#header-3">Jump</a>
which links to something like <h3 id="header-3">Header</h3>
. That’s totally fine, until you have a position: fixed;
header at the top of the page obscuring the header you’re trying to link to!
Fixed headers have a nasty habit of hiding the element you’re trying to link to.

There used to be all kinds of wild hacks to get around this problem. In fact, in the design of CSS-Tricks as I write, I was like, “Screw it, I’ll just have a big generous padding-top
on my in-article headers because I don’t mind that look anyway.”
But there is actually a really straightforward way of handling this in CSS now.
h3 {
scroll-margin-top: 5rem; /* whatever is a nice number that gets you past the header */
}
We have an Almanac article on it, which includes browser support, which is essentially everywhere. It’s often talked about in conjunction with scroll snapping, but I find this use case even more practical.
Here’s a simple demo:
In a related vein, that weird (but cool) “text fragments” link that Chrome shipped takes you to the middle of the page instead, which I think is nice.
I am showing that you can not use scroll-margin on any IE or Edge. Am I missing something? https://caniuse.com/#search=scroll-margin
Do you mean, like, you’d never consider using this because that chart says IE isn’t supported? If so, fair enough, some people have a lot of IE 11 users still and can’t ignore that. Edge is an evergreen browser though and has gone Chromium and it “works on my machine”:
So maybe the charts are wrong.
Most Windows users still don’t have Chromium Edge. You either have to manually download the new version or wait for it to arrive via Windows Update, and it seems they are doing a phased roll-out. My work laptop shows that it’s currently up to date, but it still has the older Edge.
However, considering that it will likely be rolled out to everyone in the coming months, it’s a moot point, especially considering how long we’ve had to deal with fixed headers covering up in-page links. I’m very happy to have this new CSS rule.
Awesome find. I hate using fixed menu’s because of this.
Is this supposed to work on Mobile Safari? I just tried the CodePen Demo on iOS Safari (iPhone 11 Pro) and the headings were still covered by the fixed header…
Regardless…awesome tip…will try it on a desktop browser when I have a chance…
Works fine on Firefox for android here.
It doesn’t seem to work (or be supported) in my Safari Mac (v13) and doesn’t work in latest Edge for me either (not that it’s a big problem as the method does no harm).
The method won’t be much good for responsive (fixed or sticky) headers where text wraps or content adjusts on screen width (or text zoom) so use cases will have to be properly managed.
It’s a useful method though for small fixed height headers so thanks for the tip :).
I can’t get that to work either. I’m using mobile Safari on (currently latest) iOS 13.3.1.
No, it does not, even though CanIUse says you can. I was fiddling with this a lot but I ended up with filling a bug to browser-compat-data: https://github.com/mdn/browser-compat-data/issues/4945 (more info there)
It’s a great feature, but until Safari fix it, still you can’t use it if you need to support Safari and probably need some hacks like negative top offset on ::before
Props to Miriam, I remember she tweeted about this the other day and you were astonished this existed. I was too.
Ooh, this is neat. So to achieve something similar to Chrome’s text fragment links, you can set
scroll-margin-top: 50vh
? Don’t know if you’d ever actually want that, but it’s good to know!Is it supposed to work in Edge and IE11? caniuse.com says “no”. Is there a polyfill or workaround?
@Chris thanks for sharing – this is
Regarding the text fragment do you have info on how this can be implemented without breaking anchor navigation on older (almost all) browsers?
The specs 4.3 describes a fallback if the document has changed but that does not solve the underlying problem of this not being backward compatible.
I hope I am mistaken :)
This doesn’t work on Opera for Windows. (You probably guessed that already, but just reporting from the field. :)
This doesn’t seem to be adhered to when you tab through the page using your keyboard. Shouldn’t scroll-margin/padding be applied there too?
scroll-padding
is also worth a mention here, and a bit better suited to this particular use-case. You apply it to the scroll-container rather than the target elements. I use scroll-padding when everything in a container needs to be offset, and scroll-margin for individual target offsets.For those complaining about lack of IE11 support or that Chromium Edge isn’t on every computer: This doesn’t take away from the experience, it only enhances up-to-date browsers.
So what exactly are you complaining about?
Would be great to have this work for non-fixed height headers.. Something like:
Which would adjust the margin automatically based on the current height of the #header element.
The support is not quite universal right now, but already pretty good.
Thanks for this nice trick !
Is this clever or do I miss something?
Sadly Safari does not support scroll-margin outside scroll-snapping containers.
we could immitate this margin effect for oldIE too:
:target::before {
content: "";
display: block;
position: relative;
bottom: 0;
width: 0;
height: 5rem; /* height of the header */
margin-top: -5rem;
}
Thanks! You saved me.