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 is a skip link?
A skip link lets keyboard users skip over groups of interactive elements. You can try this yourself on a lot of pages around the web—this one included! If you load this page and press the Tab key, your focus will be set to a skip link titled “Skip to main content”. Activating this link will move your focus to the main content and past the links in the navigation bar, no extra presses needed!
Websites use this technique to make life a little bit easier for people who rely on keyboards or assistive technologies.
Skip links use a function that’s built in to your browser.
All that is required for a skip link is an anchor that points to an id on the page (like <a href="#main">
) and an element with that id (like <main id="main">
).
Activating the anchor will move focus to the target element.
The big benefit is that your next press of the Tab key will set focus to the first focusable element (such as a link, button, or form field) within the target element. You’ve skipped over the repetitive focusable elements earlier in the page (such as those navigation links), hence the name.
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!
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 of0px
, 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.