The Simple Way to Offset Anchor Links

pikaso background

Ever click an anchor link and have it jump to exactly the top of the page, squished right under your fixed header? Not ideal.

You might have a sticky menu, an announcement banner, or you just want some breathing room. Whatever the reason, you need to offset that anchor.

The Problem

When you navigate to yoursite.com/page#section, the browser scrolls so that element is flush with the top of the viewport. This often means:

  • Content hidden behind fixed headers
  • Zero visual spacing (looks cramped)
  • Poor user experience

Why Other CSS Solutions Don’t Work

You might have heard about CSS properties like scroll-padding-top or scroll-margin-top. In theory, these are perfect for this exact problem:

html {
    scroll-padding-top: 80px;
}

/* or */

#target-section {
    scroll-margin-top: 80px;
}

And they do work… most of the time. The problem? Browser support is still inconsistent, especially with smooth scrolling, JavaScript scroll implementations, and certain mobile browsers. The scroll-padding-top property is great when it works, but it’s still a bit buggy across different contexts.

Hopefully we can use it everywhere soon. Until then, we need a reliable workaround.

The Solution

Use a CSS pseudo-element to create an invisible anchor point above your section. This approach works across all browsers and has been reliable for years.

One-Off Usage (Inline CSS)

If you only need this on one or two elements, add it inline:

<section id="target-section" style="position: relative;">
<style>
#target-section::before {
    content: '';
    display: block;
    position: relative;
    width: 0;
    height: 0;
    top: -60px;
    visibility: hidden;
}
</style>

Multiple Elements (Stylesheet)

For reuse across multiple pages, add this to your main stylesheet as a class, then override the offset per ID:

.anchor-offset {
    position: relative;
}

.anchor-offset::before {
    content: '';
    display: block;
    position: relative;
    width: 0;
    height: 0;
    visibility: hidden;
}

#target-section::before {
    top: -120px;
}

#about-us::before {
    top: -80px;
}

#contact-form::before {
    top: -100px;
}

Then add the class to each anchor target:

<section id="target-section" class="anchor-offset">
<div id="about-us" class="anchor-offset">
<div id="contact-form" class="anchor-offset">

Handling Mobile

Adjust offsets for smaller headers with media queries:

@media (max-width: 768px) {
    #target-section::before {
        top: -80px;
    }
}

No JavaScript, no extra divs, works everywhere. Done.