Two lines so anchor links don't hide under your header
You add a sticky header, ship some in-page anchor links, and a week later someone reports that clicking a link "scrolls to the wrong place." It doesn't, really. The browser scrolls the target to the very top of the viewport, which is exactly where your sticky header is sitting. The heading ends up tucked behind it.
Two short rules fix it:
html {
scroll-behavior: smooth;
}
[id] {
scroll-margin-top: 60px;
}Toggle the offset below and click the links. With it off, every heading lands under the bar:
Intro
Section 1. Click a link above and watch where this heading lands. With the offset off, the sticky bar covers it.
Install
Section 2. Click a link above and watch where this heading lands. With the offset off, the sticky bar covers it.
Usage
Section 3. Click a link above and watch where this heading lands. With the offset off, the sticky bar covers it.
FAQ
Section 4. Click a link above and watch where this heading lands. With the offset off, the sticky bar covers it.
What each line does
scroll-behavior: smooth on the root makes anchor jumps animate instead of
teleport. Small thing, but it tells the eye where it just went.
scroll-margin-top is the real fix. It reserves space at the top of an element
for scrolling purposes only, so when the browser scrolls that element into
view it stops short by that much. Set it to your header height (a bit more if
you want breathing room) and the heading clears the bar every time. The [id]
selector applies it to anything you can link to, so you don't have to remember
per heading. It also kicks in for keyboard focus and the browser's
find-on-page, not just clicks.
A couple of notes
Respect motion preferences. Wrap the smooth scroll so people who've asked for less motion don't get it:
@media (prefers-reduced-motion: reduce) {
html { scroll-behavior: auto; }
}And if your header height changes (taller on mobile, say), drive the offset off
a variable instead of hard-coding 60px:
:root { --header: 56px; }
[id] { scroll-margin-top: calc(var(--header) + 8px); }That's the whole thing. It's the kind of fix nobody notices when it's there, and everybody notices when it's missing.
Ask your agent to implement this
Read the full writeup at https://seangeng.com/writing/anchor-links-that-dont-hide.md and implement it in my project.
It covers: Two lines so anchor links don't hide under your header — Smooth in-page scrolling plus scroll-margin-top, so jumping to a heading lands it below your sticky header instead of tucked behind it. The fix everyone forgets until a link feels broken.
Requirements:
- Follow the technique/approach exactly as described in the writeup.
- Adapt names, colors, and styling to my project's existing conventions.
- If it's a component, make it reusable with sensible props and TypeScript types.
- Keep it accessible: semantic HTML, keyboard support, and respect prefers-reduced-motion.
- When done, tell me which files you created or changed and how to use it.Paste into Claude Code, Codex, Cursor, or any agent. view raw .md