Skip to main content
Plant Powered Pixels

Site menu

Blog

The skip link that doesn't skip


By Plant Powered Pixels

A skip link is usually the first accessibility feature a site adds. It is the small "Skip to main content" link that lets a keyboard or screen reader user jump past the navigation instead of tabbing through it on every page. It is easy to add, which is exactly why it is easy to get subtly wrong in a way no automated check will catch.

We looked at a program site recently that had one. The markup was what you would expect: a link reading "Skip to main content" pointing at the page's main region, and a real main region for it to point at. An automated scanner passes that without complaint. The link exists, its target exists, the box is checked.

Then we used it. A keyboard user presses the skip link, and focus does not move to the content. The next press of Tab continues from the top of the page, back into the navigation the link was supposed to skip. The one feature whose entire job is to save keyboard users from the nav quietly drops them right back into it.

Two things cause this, and neither is visible to a scanner that only checks whether the link and target exist.

The first is focus. Sending the browser to an anchor moves the scroll position, but it does not move keyboard focus unless the target can hold focus. A landmark like the main region cannot, by default. The fix is one attribute:

<main id="main-content" tabindex="-1">

With tabindex of negative one, the target can receive focus without becoming a tab stop of its own, so the skip link actually lands the user inside the content.

The second is the header. Many sites have a header fixed to the top of the window. Jump to the main region and the top of the content scrolls underneath that fixed header, so even when focus is right, the user is looking at content hidden behind the bar. The fix is to reserve room for it:

#main-content { scroll-margin-top: 6rem; }

Now the target stops below the header instead of behind it.

Neither fix is hard. The point is that the broken version and the working version pass the same automated check, because the difference between them is behavior, not markup. A scanner is very good at catching the thing that is missing. It is much worse at catching the thing that is present and does not work.

See it, don't just read about it.

We built a small before-and-after you can tab through yourself: same markup a scanner would pass twice, only one of them actually works. Try the skip link demo.

This is why we treat a passing accessibility scan as necessary and not sufficient. Our build gate runs several checks on every change, and they stop a whole class of real failures from shipping. They do not replace putting the keyboard away from the mouse and tabbing through the page the way the person who relies on it will. The skip link is the cleanest example of a broader rule: the only way to know an accessible feature works is to use it the way its user does.


Back to blog