Question
I am trying to improve how in-page anchors work on a page with a fixed header.
Because the header is fixed to the top of the page, clicking a link to an anchor causes the browser to scroll the target element to the very top of the viewport. As a result, the content I want to show ends up hidden behind the fixed header.
The header is 25px tall, and I need the anchor target to appear slightly lower so it is fully visible after scrolling.
I would prefer a solution using HTML or CSS, but a JavaScript solution would also be acceptable.
Short Answer
By the end of this page, you will understand why fixed headers can cover anchor targets, and how to fix that using modern CSS such as scroll-margin-top, older CSS workarounds, and JavaScript when needed.
Concept
In-page anchors let users jump to a specific part of a page using a URL fragment such as #contact or #pricing.
For example:
<a href="#contact">Go to Contact</a>
<section id="contact">
<h2>Contact</h2>
</section>
When the link is clicked, the browser scrolls the element with id="contact" into view. The problem is that browsers typically align that target near the top of the viewport.
If your layout has a fixed header, that header stays pinned to the top while the page scrolls. This means the browser may correctly scroll to the anchor, but the visible content can still be hidden underneath the header.
This matters in real projects because fixed navigation bars are common in:
- documentation sites
- landing pages
- dashboards
- single-page websites
- long articles with table-of-contents links
A good anchor offset improves usability, accessibility, and navigation. Users should land on the section heading itself, not on a position where the heading is blocked.
Mental Model
Think of an anchor jump like placing a sticky note at the top edge of a window.
The browser says, "Put this section at the top."
But your fixed header is like a strip of cardboard taped across the top of that window. The section still reaches the top, but part of it is hidden behind the cardboard.
Offsetting the anchor means telling the browser:
"Stop scrolling a little earlier so the section appears just below the fixed header."
So instead of aiming for the absolute top of the page, you aim for top minus header height.
Syntax and Examples
The best modern CSS solution is usually scroll-margin-top.
Modern CSS solution
<nav class="header">
<a href="#about">About</a>
</nav>
<section id="about" class="anchor-section">
<h2>About</h2>
<p>This section will not hide behind the fixed header.</p>
</section>
.header {
position: fixed;
top: 0;
left: 0;
right: 0;
height: 25px;
}
.anchor-section {
scroll-margin-top: 25px;
}
What this does
Step by Step Execution
Consider this example:
<a href="#team">Team</a>
<header class="header">My Header</header>
<section id="team" class="anchor-section">
<h2>Team</h2>
<p>Meet the team.</p>
</section>
.header {
position: fixed;
top: 0;
height: 25px;
}
.anchor-section {
scroll-margin-top: 25px;
}
Here is what happens step by step:
- The user clicks the link with
href="#team". - The browser looks for the element with
id="team". - The browser starts scrolling that element into view.
Real World Use Cases
This pattern appears in many common interfaces.
Documentation pages
A table of contents links to sections like #installation or #api, while a fixed top navigation bar remains visible.
Marketing or landing pages
Buttons like "Features", "Pricing", and "Contact" jump to sections on the same page.
Admin dashboards
A fixed app header or toolbar can overlap section headings when navigating within long settings pages.
Help centers and FAQs
Users click question links and should land with the full heading visible.
Single-page portfolios
Navigation links jump to About, Projects, and Contact sections while the header stays fixed for easy navigation.
Real Codebase Usage
In real projects, developers usually solve this in one of these ways:
1. Use CSS on anchor targets
A common pattern is to apply scroll-margin-top to headings or sections that may be linked to.
section[id],
h2[id],
h3[id] {
scroll-margin-top: 64px;
}
This works well in documentation sites and component libraries.
2. Use a CSS custom property for header height
This makes maintenance easier.
:root {
--header-height: 25px;
}
.header {
height: var(--header-height);
}
[id] {
scroll-margin-top: var(--header-height);
}
If the header height changes, you update it in one place.
3. Combine with smooth scrolling
html {
scroll-behavior: smooth;
}
This improves the user experience without JavaScript.
4. Use JavaScript for dynamic layouts
Common Mistakes
1. Forgetting that the header height may change
A hard-coded 25px works only if the header is always exactly 25px tall.
Better:
:root {
--header-height: 25px;
}
[id] {
scroll-margin-top: var(--header-height);
}
2. Applying the offset to the wrong element
Broken approach:
.header {
scroll-margin-top: 25px;
}
This does nothing for anchor targets because the offset belongs on the element being scrolled to, not on the fixed header.
3. Using extra empty elements unnecessarily
Older solutions often inserted hidden anchor elements just to create spacing. This works, but it makes HTML harder to maintain.
Prefer modern CSS when possible.
4. Forgetting browser behavior with JavaScript links
Broken code:
link.addEventListener('click', () => {
window.scrollTo({ top: 100 });
});
Comparisons
| Approach | Best for | Pros | Cons |
|---|---|---|---|
scroll-margin-top | Modern browsers and clean CSS solutions | Simple, readable, no extra markup | Depends on browser support in older environments |
padding-top + negative margin-top | Older CSS workaround | No JavaScript needed | Can affect layout and is less intuitive |
JavaScript scrollTo() | Dynamic headers or custom behavior | Full control, can support smooth scrolling and dynamic offsets | More code, more maintenance |
| Empty anchor spacer element | Legacy pages | Works in simple cases | Adds unnecessary markup |
CSS vs JavaScript
Cheat Sheet
/* Fixed header */
.header {
position: fixed;
top: 0;
height: 25px;
}
/* Best modern fix */
[id] {
scroll-margin-top: 25px;
}
/* Optional smooth scrolling */
html {
scroll-behavior: smooth;
}
/* Reusable with a variable */
:root {
--header-height: 25px;
}
[id] {
scroll-margin-top: var(--header-height);
}
/* Older workaround */
.offset-anchor {
padding-top: 25px;
margin-top: -25px;
}
/* JavaScript manual offset */
const headerOffset = 25;
const elementTop = target.getBoundingClientRect().top + window.;
.({ : elementTop - headerOffset, : });
FAQ
How do I stop a fixed header from covering anchor links?
Use scroll-margin-top on the target element, with a value equal to the fixed header height.
Is there a pure CSS way to offset anchor scrolling?
Yes. The preferred modern solution is scroll-margin-top. An older workaround is padding-top with a matching negative margin-top.
Should I use JavaScript to offset anchors?
Only if CSS is not enough, such as when the header height changes dynamically or you need custom scrolling behavior.
What is the difference between scroll-margin-top and margin-top?
margin-top changes layout spacing. scroll-margin-top changes where the browser positions an element during scrolling.
Can I apply anchor offset to all elements with IDs?
Yes, many projects use:
[id] {
scroll-margin-top: 25px;
}
But make sure that broad rule does not affect elements you do not want to target.
Does smooth scrolling fix the overlap problem?
No. Smooth scrolling changes the animation, not the final position. You still need an offset solution.
Mini Project
Description
Build a simple single-page site with a fixed header and navigation links that jump to sections such as About, Services, and Contact. The project demonstrates how to make anchor navigation work correctly without hiding section headings behind the fixed header.
Goal
Create a page where in-page navigation links scroll to visible section headings even when a fixed header remains at the top.
Requirements
- Create a fixed header with at least three navigation links.
- Add at least three sections that can be reached with anchor links.
- Prevent section headings from being hidden behind the fixed header.
- Add smooth scrolling for a better user experience.
- Keep the solution in HTML and CSS only.
Keep learning
Related questions
Angular ngClass Conditional Class Binding Explained
Learn how to use Angular ngClass for conditional classes, fix common binding mistakes, and understand why this template error happens.
CSS :not() Selector for Elements Without a Class or Attribute
Learn how to use the CSS :not() selector to target elements that do not have a specific class or attribute, with examples and common pitfalls.
CSS Font Scaling Relative to Container Size: %, em, rem, vw, and Responsive Text
Learn how CSS font scaling really works and how to make text responsive using %, em, rem, vw, clamp(), and media queries.