Skip to main content

Short note on skip links with sticky headers

TL;DR Prevent your sticky header from obscuring your main content with some scroll-padding on the <html> element.

What we want to prevent: our heading being chopped in half by our sticky header.

How to make skip links work with sticky headers

A common issue with this technique is that, by default, the target element is scrolled to the top of the viewport when the skip link is activated. This is an issue if the page has a fixed element—such as a sticky header—that obscures the top of the viewport. Luckily, there’s a CSS property to fix this: scroll-padding!

scroll-padding specifies offsets that lets you tell the browser which parts of your page are obscured by things like UI elements (such as a sticky header). Like padding and margin, scroll-padding is a shorthand property. This means that you can also use the longhand version to define specific sides, like so: scroll-padding-top: 2em;.

To use scroll-padding, we’ll need to know how much space we need to bridge. The sticky header on this page is 5rem high. To get the browser to skip this part when it calculates where to scroll the page to, we use scroll-padding-top: 5rem;.

The padding specified by scroll-padding will be applied to the ‘scroll container’ that it’s added to. My understanding of this part of the specification is a bit fuzzy (feel free to explain it to me). But it seems like this would be the <html> element for skip links as described and used in this article.

The Code

Our CSS would end up looking something like this:

html {
    scroll-padding-top: 5em;
}

HTML:

<html>
    <header>
        <a href="#main-content">Skip to main content</a>
    </header>
    <main id="main-content" tabindex="-1">
        <h1>Fancy title for your content</h1>
    </main>
</html> 

If you want, you can play around with the CodePen version of this article! You can also read my comments in the annotated CSS file.

The Result

If we did everything right, our heading (or other content) should no longer be chopped in half when we activate a skip link!

What we wanted to achieve: the newly focused content is visible in full; no more overlapping from the sticky header.

Browser Support

Seems to work everywhere except for Internet Explorer 11.

Couldn’t browsers calculate this for us?

The short answer is “probably not”—though wouldn’t that be a great feature to have?

The CSS Scroll Snap specification—which scroll-padding is defined in—mentions an auto value that might at first seem helpful, but it turns out that auto is merely scroll-padding’s default value:

[auto] indicates that the offset for the corresponding edge of the scrollport is UA-determined. This should generally default to a used length of 0px, but UAs may use heuristics to detect when a non-zero value is more appropriate.

The wording here means that while browsers are allowed to calculate scroll-padding values other than 0px, it means they almost certainly won’t.

With that in mind, your best bet is to specify scroll-padding values yourself. Who knows what the future might hold, though.

Related resources