Why Is My Fixed Header Covering Content?

A fixed header covers content because position:fixed removes the header from normal document flow. The content below it does not automatically move down, so the first section, page title, or anchor target can slide underneath the header.

Fixed Header Layout Fix

Why Is My Fixed Header Covering Content?

A fixed header can look normal at first, but then the hero title sits behind it, anchor links land too high, or the top of the page feels hidden. The browser is not making a mistake. A fixed element is pinned to the viewport and no longer reserves layout space in the document.

  • Fixed header overlap
  • Anchor link offset
  • Sticky vs fixed layout

What the bug looks like

The page title hides behind the header, the hero looks cut off, or clicking a menu link lands with the heading covered.

Why it happens

Fixed elements do not reserve space in normal layout. The content starts at the top as if the header were not there.

What fixes it

Add a deliberate top offset, handle anchor scrolling, adjust mobile header height, or use position:sticky when it fits the layout better.

The simple rule behind fixed header overlap

A fixed header is removed from normal document flow. That means the browser paints the header on top of the viewport, but the next section still begins where it normally would: at the top of the page.

This is why the first heading can look like it disappeared. It is usually not missing. It is sitting underneath the header.

The fastest fix is to measure the real header height and create a matching offset for the content and scroll targets.

Error 1

The content has no top offset

This is the classic fixed header bug. The header is fixed to the top of the viewport, but the main content still begins at the top of the document.

Broken code

No offset
.site-header {
  position: fixed;
  top: 0;
  left: 0;
  width: 100%;
  height: 80px;
  z-index: 100;
}

main {
  /* no top offset */
}

Broken visual result

Content starts under the header
Fixed header
Hero title is partly hidden The content began at the top, underneath the fixed header.

Correct code

Add top offset
.site-header {
  position: fixed;
  top: 0;
  left: 0;
  width: 100%;
  height: 80px;
  z-index: 100;
}

main {
  padding-top: 80px;
}

Fixed visual result

Content starts below the header
Fixed header
Hero title is visible The main content now reserves space for the fixed header.
Error 2

The header height is duplicated in several places

Hard-coding the same header height in multiple rules works at first, but it becomes fragile. If the header height changes, one value is easy to forget.

Broken code

Repeated values
.site-header {
  height: 80px;
}

main {
  padding-top: 80px;
}

section {
  scroll-margin-top: 96px;
}

Broken visual result

Offsets can drift apart
Header 80px
Mixed spacing rules Different values make future changes risky.

Correct code

Single source of truth
:root {
  --header-height: 80px;
  --header-offset: calc(var(--header-height) + 16px);
}

.site-header {
  height: var(--header-height);
}

main {
  padding-top: var(--header-height);
}

section {
  scroll-margin-top: var(--header-offset);
}

Fixed visual result

One value controls the layout
Header variable
Offsets stay consistent Changing the header height becomes safer.
Error 3

Anchor links land behind the fixed header

You may fix the top of the page and still have broken in-page navigation. The browser scrolls the target to the top of the viewport, where the fixed header is waiting.

Broken code

No scroll offset
<a href="#features">Features</a>

<section id="features">
  <h2>Features</h2>
</section>

Broken visual result

Anchor target is covered
Fixed header
#features The target lands behind the fixed header.

Correct code

Scroll margin
section,
h2,
h3 {
  scroll-margin-top: 96px;
}

Fixed visual result

Anchor target has breathing room
Fixed header
#features The target lands below the header instead of behind it.
Error 4

The mobile header is taller than the desktop header

A header that is 72px on desktop may become 112px on mobile after nav items wrap or a second row appears. If the offset stays desktop-sized, mobile content gets covered.

Broken code

Desktop-only offset
:root {
  --header-height: 72px;
}

main {
  padding-top: var(--header-height);
}

Broken visual result

Mobile header is taller
Fixed header
DocsFixesCSSHTMLResponsive
Content is too high The mobile header grew, but the offset did not.

Correct code

Responsive offset
:root {
  --header-height: 72px;
}

@media (max-width: 768px) {
  :root {
    --header-height: 112px;
  }
}

main {
  padding-top: var(--header-height);
}

Fixed visual result

Mobile offset matches header
Fixed header
DocsFixesCSSHTMLResponsive
Content starts below The mobile offset now matches the taller header.
Error 5

Using fixed when sticky would be simpler

Many fixed header bugs happen because the header did not need to be fixed in the first place. If the header only needs to stick after reaching the top, position:sticky can be cleaner.

More fragile code

Fixed requires offset
.site-header {
  position: fixed;
  top: 0;
  height: 80px;
}

main {
  padding-top: 80px;
}

Fixed header behavior

Needs manual compensation
Fixed header
Works, but requires offset Fixed is valid, but you must manage the layout space.

Alternative code

Sticky header
.site-header {
  position: sticky;
  top: 0;
  z-index: 100;
}

Sticky visual result

Header keeps layout space
Sticky header
Content begins naturally The header still participates in normal document flow before sticking.

Fast practical rule

If a fixed header covers content, do not guess random margins. Measure the real header height, offset the content intentionally, and protect anchor links with a scroll offset.

Recommended baseline

Fixed header foundation

This baseline keeps the header height, page offset, and anchor offset connected through custom properties.

:root {
  --header-height: 80px;
  --anchor-offset: calc(var(--header-height) + 16px);
}

.site-header {
  position: fixed;
  inset: 0 0 auto 0;
  height: var(--header-height);
  z-index: 100;
}

main {
  padding-top: var(--header-height);
}

section,
h2,
h3 {
  scroll-margin-top: var(--anchor-offset);
}

@media (max-width: 768px) {
  :root {
    --header-height: 104px;
  }
}

Why this baseline helps

It compensates for fixed positioning The main content gets space equal to the header height.
It protects anchor navigation scroll-margin-top keeps headings from landing behind the header.
It handles mobile height changes The same variable can change at mobile breakpoints.
It is easier to maintain You adjust one header value instead of chasing scattered magic numbers.

Debug checklist

  • Check whether the header uses position:fixed.
  • Measure the actual rendered header height in DevTools.
  • Add matching top padding to main, the content wrapper, or the first section.
  • Check whether the header height changes on mobile.
  • Test in-page anchor links like #features, #pricing, or #faq.
  • Use scroll-margin-top or scroll-padding-top for anchor navigation.
  • Check whether a fixed header is really needed or whether position:sticky is enough.
  • Avoid random margin guesses that only work at one screen size.
Best first move Measure the real header height and apply a deliberate top offset to the content.
Most common false fix Increasing z-index when the real problem is that the content starts under the header.
Most overlooked cause Anchor links can still hide headings behind the header even after the first page load looks fixed.
Better mindset Fixed headers are not wrong. They simply require layout compensation because they leave document flow.

When z-index is also involved

Sometimes the header covers content and also sits above dropdowns or modals. In that case, you may be dealing with a layering problem too.

If the issue involves elements appearing behind each other, read Why is my z-index not working?.

When responsive layout is the real problem

If the header only covers content on mobile, the layout may need a mobile-specific header height or responsive offset.

If multiple sections are breaking on mobile, read Why is my responsive design not working?.

When absolute elements are also misplaced

Fixed headers, dropdowns, badges, and menus often appear together. If an overlay is not only covered but also positioned in the wrong place, the parent positioning context may be wrong.

If that sounds familiar, read Why is my absolute positioned element in the wrong place?.

When overflow creates side effects

A fixed header can make layout issues more visible, but horizontal overflow usually comes from a wide element elsewhere on the page.

If the page also scrolls sideways, read Fix overflow causing horizontal scroll.

Final takeaway

A fixed header covers content because it is removed from normal document flow. The page content does not automatically reserve space for it.

Fix the layout by adding a top offset that matches the header height, using scroll offsets for anchor links, adjusting the value on mobile, and considering position:sticky when a fully fixed header is not necessary.

Once the content and scroll targets know the header exists, the overlap bug becomes predictable and easy to avoid.

Need more layout fixes?

Browse the responsive cluster or jump back to the full FrontFixer library to keep debugging faster.

Fix position: sticky not working

Position: sticky usually stops working when the sticky element has no offset value, a parent creates the wrong scroll container, or the surrounding layout gives the element no real space to stick.

CSS Sticky Fix

Fix position: sticky not working.

If your sticky header, sidebar, filter bar, table header, or navigation element refuses to stick, the problem is usually not the position:sticky declaration by itself. Sticky only works when the browser can calculate a scroll threshold, a containing area, and enough movement space. If one parent in the chain has the wrong overflow, height, alignment, or scroll behavior, the sticky element can act like normal position:relative.

  • Sticky headers
  • Sticky sidebars
  • Overflow traps
  • Scroll container bugs

What the bug looks like

The element scrolls away like normal content, sticks only for a tiny moment, stops too early, or behaves differently inside a sidebar, table, header, or dashboard layout.

Why it happens

Sticky is not just a property. It is a relationship between the sticky element, its offset, its ancestors, and the scroll container that controls the page movement.

What usually fixes it

Add a clear offset, remove accidental parent overflow, give the sticky element enough vertical space, and make sure the layout is not stretching the sticky column incorrectly.

The simple rule behind position:sticky

A sticky element behaves like a normal element until the page reaches a defined offset. Then it sticks inside the boundaries of its containing area. That means sticky needs two things at the same time: a normal layout position and a scroll threshold.

This is why position:sticky can feel confusing. It is not fully like fixed, because it does not attach to the viewport forever. It is not fully like relative, because it can temporarily stick during scroll. Sticky lives between normal flow and fixed positioning.

Error 1

The sticky element has no offset value

This is the first thing to check. A sticky element needs a threshold such as top:0, top:20px, or sometimes bottom:0. Without that threshold, the browser has no clear point where sticky behavior should begin.

Broken code

No threshold
.sidebar {
  position: sticky;
}

Broken visual result

It scrolls away
Sidebar / filter bar

The element is sticky in name, but there is no offset telling the browser when to stick.

Correct code

Sticky threshold
.sidebar {
  position: sticky;
  top: 24px;
}

Fixed visual result

It has a point to stick
Sticky sidebar / filter bar

The browser now knows the sticky element should stop when it reaches 24px from the top.

Error 2

A parent has the wrong overflow

This is the sticky bug that wastes the most time. You add position:sticky and top:0, but it still does nothing. Then you inspect the parent chain and discover a wrapper with overflow:hidden, overflow:auto, or overflow:scroll.

Broken code

Overflow trap
.page-shell {
  overflow: hidden;
}

.sidebar {
  position: sticky;
  top: 24px;
}

Broken visual result

Wrong scroll container
Sticky element

A parent wrapper is controlling overflow, so sticky may be trapped inside the wrong scroll context.

Correct code

Cleaner parent
.page-shell {
  overflow: visible;
}

.sidebar {
  position: sticky;
  top: 24px;
}

Fixed visual result

Parent no longer traps it
Sticky element

Once the accidental overflow rule is removed or moved to the right element, sticky has a cleaner scroll context.

Error 3

The parent is too short for sticky to become visible

Sticky cannot stick forever. It only sticks inside the area of its parent or containing block. If the parent ends almost immediately, the sticky element has no useful distance to travel. This makes sticky look broken even when the CSS syntax is correct.

Broken code

Short parent
.sidebar-wrap {
  height: 120px;
}

.sidebar {
  position: sticky;
  top: 24px;
}

Broken visual result

No room to stick
Sticky sidebar
The parent ends too soon, so the sticky behavior has almost no visible runway.

The element may technically enter sticky mode, but there is not enough parent height to see it working.

Correct code

Enough space
.layout {
  display: grid;
  grid-template-columns: 1fr 320px;
  gap: 24px;
}

.sidebar {
  position: sticky;
  top: 24px;
  align-self: start;
}

Fixed visual result

Enough scroll area
Sticky sidebar
The sticky element now has a meaningful containing area during scroll.

Sticky becomes easier to understand when the parent has enough height for the element to move and stop.

Error 4

Grid or Flexbox is stretching the sticky element

Sticky sidebars often fail visually inside Grid or Flexbox because the browser stretches columns or items by default. The sticky element may become as tall as the row, or the sidebar column may not behave like a natural scroll companion. In many real layouts, align-self:start is the missing piece.

Broken code

Stretched item
.layout {
  display: grid;
  grid-template-columns: 1fr 320px;
}

.sidebar {
  position: sticky;
  top: 24px;
}

Broken visual result

Layout fights sticky
Sidebar feels stretched or unreliable

The sticky item is inside a layout context, but the alignment is not helping it behave like a compact sticky block.

Correct code

Start aligned
.layout {
  display: grid;
  grid-template-columns: 1fr 320px;
  gap: 24px;
  align-items: start;
}

.sidebar {
  position: sticky;
  top: 24px;
  align-self: start;
}

Fixed visual result

Sticky behaves like a block
Sticky sidebar

Start alignment prevents the sidebar from being stretched in a way that makes sticky behavior harder to reason about.

Error 5

You actually need position:fixed, not sticky

Sometimes sticky is not the right tool. If you want the element to stay attached to the viewport all the time, even after its parent ends, sticky may disappoint you. Sticky is bounded by its container. Fixed positioning is viewport-based.

Wrong expectation

Container bound
.floating-help {
  position: sticky;
  top: 24px;
}

Why this feels broken

Sticky only sticks inside its containing area. If the parent ends, the sticky behavior ends too. That is correct behavior, not a browser bug.

Use fixed when needed

Viewport bound
.floating-help {
  position: fixed;
  right: 24px;
  bottom: 24px;
}

When fixed is better

Use fixed for floating help buttons, persistent chat buttons, cookie banners, or elements that should stay attached to the viewport regardless of their parent.

Fast practical rule

If position:sticky is not working, do not start by adding random z-index values. Sticky failure is usually about offset, overflow, scroll context, parent height, or alignment. First check top. Then inspect every parent for overflow. Then check whether the sticky element has enough room to stick.

Sticky vs fixed: the key difference

position:sticky keeps the element in normal flow first, then lets it stick during scroll within a specific boundary. position:fixed removes the element from normal flow and attaches it to the viewport.

This difference matters because a fixed header can cover content if you do not reserve space for it, while a sticky header usually keeps its original space in the document flow.

Good sticky header pattern

Stable
.site-header {
  position: sticky;
  top: 0;
  z-index: 20;
  background: white;
}

This pattern works well when the header should scroll normally at first and then stay visible at the top while the user continues down the page.

Sticky sidebar pattern

Production-minded
.content-layout {
  display: grid;
  grid-template-columns: minmax(0, 1fr) 320px;
  gap: 32px;
  align-items: start;
}

.sidebar {
  position: sticky;
  top: 24px;
  align-self: start;
}

Why this pattern is safer

The main content column can shrink safely because it uses minmax(0,1fr). The sidebar gets a fixed track, but it is aligned to the start instead of being stretched. The sticky element has a clear top threshold and a predictable layout context.

This is the kind of pattern that survives real pages better than a tiny demo that only works with perfect content.

Debug checklist

  • Confirm the element has position:sticky, not a typo like position: stickyed or a class that is not applied.
  • Add a clear offset such as top:0, top:16px, or top:24px.
  • Inspect every parent for overflow:hidden, overflow:auto, overflow:scroll, or unusual shorthand overflow rules.
  • Check whether the sticky element is inside a parent that is too short for sticky behavior to become visible.
  • If the sticky element is inside Grid or Flexbox, test align-self:start or align-items:start.
  • Check whether another wrapper is creating the real scroll container instead of the page.
  • Do not use sticky when you really need viewport-level behavior. Use position:fixed for persistent floating UI.
  • Add a background and a useful z-index only after sticky is actually working.
Best first move Add top and test again before changing anything else.
Most common false fix Increasing z-index when the real issue is parent overflow or missing offset.
Most overlooked cause The parent is too short, so sticky has no visible area where it can stay stuck.
Better mindset Sticky is a scroll relationship. Debug the parent chain, not just the element.

Final takeaway

position:sticky fails when the browser cannot calculate a useful sticky relationship. The usual cause is not a mysterious browser bug. It is a missing offset, a parent with overflow, a container that is too short, or a layout context that stretches or traps the sticky element.

Start with the basics: add top, remove accidental overflow from parents, give the element enough scroll space, and align sticky sidebars to the start in Grid or Flexbox layouts. Once the surrounding structure is clean, sticky becomes predictable instead of frustrating.

Want more fixes like this?

Explore the full FrontFixer fixes library and keep debugging with practical guides built for real front-end layout problems.