Skip Links That Survive a Sticky Header

November 12, 2026

A skip link jumps to the main content, and a sticky header paints over the landing spot. Give the skip target scroll-margin-top equal to the header height, or set scroll-padding-top on the scroll container, and the jump lands below the bar. Test it with Tab, Enter, and your eyes.

The jump that lands under the bar

Ship the textbook skip link: first focusable element in the document, "Skip to content", jumping to the main landmark. Feel virtuous about WCAG 2.4.1 Bypass Blocks. Then make the navigation sticky, and every Tab-then-Enter lands the heading under sixty-four pixels of chrome. The link still works, in the narrow sense that focus moved. The user cannot see where it moved to, because the header is on top of it. Sighted keyboard users notice immediately. Mouse users never hit the skip link, so it survives code review for years.

The browser did what it was asked. The main element is at the top of the viewport, and the header is also at the top of the viewport. The header wins, because it paints later and sits higher in the stacking order. The failure is not in the skip link. It is in the assumption that the top of the viewport is empty space. On a storefront with a sticky header, it never is.

The fix, and which one to pick

There are two CSS-native fixes, and they do the same job from different ends. scroll-margin-top goes on the target: give the main element, and the headings, a scroll margin equal to the header height, and the browser leaves that much room above the target when it scrolls to it. scroll-padding-top goes on the scroll container, usually the root element, and reserves the same space for every scroll-into-view on the page, including focus moves and anchor jumps. For a document-level sticky header, scroll-padding on the root is the simpler choice, because it covers every target at once.

Make the header height a token, not a magic number. Set a CSS variable to the header's used height, fourrem or whatever the theme measures, and have both the header and the scroll padding read it. When the header grows on mobile, or when a promo bar appears above it, the token changes in one place and every jump still lands clear. The alternative, extra padding on the main element forever, wastes space on every page load for a problem that only exists at the moment of the jump.

The thirty-second test

Tab once, press Enter on the skip link, and look. The first heading of the main content should be fully visible, below the sticky bar, with no part of it hidden. Then tab through the page and watch the focused element: it should never disappear entirely under the header, the cookie banner, or a chat widget. That second check is WCAG 2.2 criterion 2.4.11 Focus Not Obscured, and the same scroll-padding fix covers it. One property, two criteria, and a skip link that finally does what its name promises. Keyboard failures cluster: mega menus that eat keyboards and the autocomplete attribute that fixes 1.3.5 in one line.

Frequently asked questions

What is a skip link for?

It is the first focusable element on the page, a link that jumps straight to the main content so keyboard and switch users do not have to Tab through the whole header on every page. WCAG 2.4.1 Bypass Blocks is the formal name.

Why does a sticky header break it?

The browser scrolls the target to the top of the viewport, and the sticky header occupies that same space. The heading lands underneath the bar, so the keyboard user cannot see where focus went.

Should I use scroll-margin or scroll-padding?

Scroll-margin-top on the target covers that one jump. Scroll-padding-top on the scroll container covers every focus move and anchor jump on the page. For a document-level sticky header, scroll-padding on the root is the simpler choice.

Does the skip link need tabindex -1 on the target?

Yes, on the main element. tabindex -1 lets the jump move programmatic focus to the landmark without putting main in the Tab order. Without it, the visual jump happens but focus stays on the link.