Skip to content
blago

BlogCSS fixes

Anchor links that land under your Webflow navbar

The browser lands the section at the very top of the window, exactly where your sticky header is sitting.

Break it yourself

A live demo. It measures its own result in your browser.

yoursite.webflow.ioPick a link

Pricing

Process

FAQ

Use the links in the demo header to jump.

When a Webflow anchor link hides the section heading under your navbar, the browser is doing what it was told: it lines the top of the target up with the top of the window, exactly where your sticky header sits. Give the targets a scroll-margin-top equal to the header height and every in-page link on the site lands below it.

The short answer

Webflow does not offset anchor jumps for a sticky navbar on its own. When someone clicks a link to #pricing, the browser scrolls until the top edge of the element with that ID meets the top edge of the viewport. A sticky or fixed navbar is drawn over that same strip, so the first part of the section, usually the heading, ends up underneath it.

The fix is one CSS value. Put scroll-margin-top on the targets, or scroll-padding-top on the html element, set to the header height plus a little space. Both tell the browser to stop short by that amount. One rule covers every anchor link, every button that points at a section, and every visitor who arrives from a URL with a hash in it.

Why Webflow anchor links land under the navbar

When the browser scrolls to a fragment, it does not account for anything pinned over the page. It aligns the target’s top edge with the top of the scroll area, and whatever is stuck to that area covers what lands there. From the browser’s side, the jump worked perfectly.

Many sites patch this with a script: catch the click, read the target’s position, subtract a number, scroll. It works on the day it is written and drifts as soon as the header changes, because nothing ties the number to the header.

That is exactly what happened on our own site. Our in-page links scrolled with a hard-coded offset of 70px, while the sticky header area was 103px tall, and no target had a scroll-margin-top. We measured the header covering the top 87px of all eight anchor targets on the page. One scroll-margin-top rule fixed all eight links and a button that scrolled to a section, and the magic number in the script went away.

How to measure the overlap

In the Webflow Designer

  1. Select the navbar and check Position in the Style panel: both Sticky and Fixed cause the overlap. Note its height, including any announcement bar above it that is pinned too.
  2. Select a target section and read its ID in the Element settings panel. That ID is what the link points at.
  3. Look at the section’s top padding. A section with generous padding may clear the header by luck, which is why only some of your anchors misbehave.

The 30 second check in DevTools

  1. Open the published page and click one of the anchor links, so the URL ends in a hash.
  2. Right-click the navbar, choose Inspect, and read its height in the Computed tab.
  3. Inspect the target and look for scroll-margin-top in Computed. If it reads 0px, nothing is offsetting the jump.
  4. Paste the snippet below into the Console, with your own header class in the first line. A positive number is the number of pixels hidden under the header.
const header = document.querySelector('.navbar');
const target = document.querySelector(location.hash);
const hidden = header.getBoundingClientRect().bottom
  - target.getBoundingClientRect().top;
console.log(Math.round(hidden) + 'px of the target is under the header');

That number, plus some breathing room, is the offset you need.

The fix: scroll-margin-top on the targets

Webflow has no dedicated setting for this, but the Style panel accepts any CSS property in its Custom Properties section at the bottom. There are two clean ways to apply the offset.

Option 1: on the targets

Add scroll-margin-top to the class your sections already use, through Custom Properties. Better still, keep the header height in one CSS variable and read it in both places, so the offset follows the header whenever it changes:

:root {
  --header-h: 96px;
}
.navbar {
  height: var(--header-h);
}
[id] {
  scroll-margin-top: calc(var(--header-h) + 16px);
}

The [id] selector matches every element with an ID, which is every possible anchor target. The Style panel styles classes, so a rule like this goes into a style tag in the head code under Site settings › Custom code, or in Page settings if only one page has anchors. The 96px and 16px here are examples: use the height you measured.

Option 2: once on the html element

html {
  scroll-padding-top: calc(var(--header-h) + 16px);
}

This sets the offset on the page’s scroll container instead of on each target. It is one line, and it covers targets you add later without any class. We use the target version when different sections need different offsets, and this one when the offset is the same everywhere.

Delete the script offset

Once the CSS is in, remove any script that subtracts a fixed number. A script that scrolls with scrollIntoView already respects the CSS offset, so a subtraction on top of it counts the header twice.

Other things that move the landing spot

The header changes height at a breakpoint

A navbar that is shorter on phones needs a shorter offset there. With the height in one variable, that is a single override of --header-h inside the matching media query, and both the header and the offset follow it.

A banner sits above the navbar

A pinned announcement bar adds to the covered strip. Measure from the top of the window to the bottom of the last pinned element, not just the navbar.

The target sits inside an overflow hidden wrapper

A box with overflow hidden can still be scrolled by an anchor jump, so the jump may shift the wrapper’s clipped content as well as the page. The same wrapper is a common reason a sticky navbar stops sticking, and the fix there, overflow clip, helps here too.

Content loads in above the target after the jump

Images without set dimensions, or embeds that load late, push the target down after the browser has already scrolled. Give images a width and height or an aspect ratio so the layout is final before the jump happens.

How to keep anchors and the header in sync

  • One number for the header height. Define it once and read it in the header and in the offset. Two copies of the same number drift apart, which is how we ended up with 70 against 103.
  • No offsets in scripts. Let CSS own the offset, and let scripts call the browser’s own scrolling.
  • Click every anchor after any header change, at every breakpoint, and run the snippet once on each. It takes a minute and gives you a number instead of an impression.

Headers change often, and anchors break silently when they do, which is why this check belongs in regular website maintenance and support rather than in a one-off launch list.

Questions people ask

  • Add scroll-margin-top to the anchor targets, equal to the navbar height plus a little space, or set scroll-padding-top on the html element once. Webflow does not offset anchor jumps on its own, so the offset has to be added as CSS.

  • On a class, through the Custom Properties section at the bottom of the Style panel. For one rule that covers every element with an ID, put a style tag in the head code under Site settings, Custom code.

  • scroll-padding-top on the html element sets one offset for the whole page. scroll-margin-top on each target lets different sections use different offsets. Both are plain CSS and both apply to links, buttons and URLs that arrive with a hash.

We fix this for clients

If your site has this problem and you would rather not touch the CSS yourself, send us the link. We reply within a few hours, or write to blago.creative.agency@gmail.com.

More notes

All notes