Component
skip-link
Demo
tokens.css,
base.css and skip-link.css. No scripts.
Source
Copy both files, or run npx nojsui add skip-link.
Browser support
Works across current and earlier versions of every major engine.
| Feature | Baseline | Chrome | Edge | Firefox | Safari | Chrome Android | Firefox Android | Safari iOS |
|---|
Usage
Place the link as the first element inside <body> — before the app bar,
before any wrapper — and point its href at the id of the page’s main
content. Both matter: put it later in the source and it is no longer the
first tab stop; give it no matching target and pressing it does nothing.
<a class="sk-skip-link" href="#main">Skip to content</a>
<!-- ... app bar, nav, everything else ... -->
<main id="main" tabindex="-1">…</main>
How it works
display: none and visibility: hidden both remove the link from the tab
order and from the accessibility tree, so it is in the markup, looks done on
a checklist, and no keyboard or screen-reader user can ever reach it. Moving
it off-screen with a transform keeps it reachable, but leaves it displaced while
the page is at rest, which is exactly what this repo’s own conformance check
reports as content stranded by a missing feature. It is right to flag that.
So this component hides with clip-path: inset(50%) instead: the box keeps
its position, its opacity, its place in the tab order — it simply is not
painted. clip-path is Baseline widely available, so nothing here depends
on a feature that might be absent.
Why this component writes a :focus-visible rule. base.css owns the
kit’s single focus ring, and that rule still holds here — this file never
touches the ring. What it styles instead is the reveal: the link’s entire
reason to exist is that focus makes it appear, so the focus state is not
decoration layered on top of the component, it is the component.
It is not the only component that writes one, and the earlier version of this
file wrongly said it was. A component writes :focus-visible when focus lands
on one element and the effect belongs on another — tabs and command-menu
forward a visually-hidden radio’s ring onto its visible label — or when focus
reveals a sibling, which tooltip does. This is the third case: focus reveals
the element itself.
Following the link moves the browser’s navigation position to the target.
For the target to actually receive focus — rather than the page merely
scrolling to it, leaving focus behind on the link — it needs tabindex="-1".
That is the detail almost every hand-rolled skip link misses: without it,
the next Tab press continues from the skip link itself instead of
from the content, which defeats the point.
Keyboard contract
This is the one component whose contract must be verified by keyboard rather than by inspection: a screenshot of the page at rest cannot show whether the link reveals on focus.
| Key | Behaviour |
|---|---|
Tab (from page load) | Moves focus to the skip link — it is the first tab stop |
Enter | Activates the link, moving focus and scroll to the main content |
Verified manually in VoiceOver and NVDA: not yet — do this before marking the component done, and change this line when you have.
Accessibility notes
- The link must be the first focusable element in
<body>— its value comes entirely from being reachable before anything else on the page. - Because it is clipped rather than removed, screen readers announce it in document order like any other link; nothing about its hiding technique hides it from assistive technology.
- The target needs
tabindex="-1"so focus actually lands on it — see “How it works” above. Without it, the destination is only scrolled to, not focused, and the nextTabcontinues from the skip link.
Theming
| Property | Default | Controls |
|---|---|---|
--sk-skip-link-bg | --sk-color-surface-raised | Background once focused |
--sk-skip-link-fg | --sk-color-text | Text colour |
--sk-skip-link-radius | --sk-radius-md | Corner radius |
--sk-skip-link-border-color | --sk-skip-link-bg (matches the chip, so invisible) | Border colour. Invisible by default; a theme that zeroes radius and shadow can set this to keep the revealed chip visible |
--sk-skip-link-z | 200 | Stacking order. Must stay above --sk-app-bar-z (100) |
Degradation
| Feature used | Baseline status | Behavior without it |
|---|---|---|
clip-path | Widely available | Nothing degrades — every supported browser paints the reveal correctly |