Field Guide / intermediate

Sticky positioning: why it fails and how to design around it

Sticky positioning works when the element has a scroll container, a threshold, and enough room to move.

position: sticky feels unreliable until you treat it as a contract. A sticky element is relative until it crosses an offset threshold inside its scroll container, then it sticks within the limits of its containing block.

When sticky fails, one of those conditions is usually missing: no offset, an unexpected scroll container, a parent that is too short, or a stretched layout item with nowhere to move.

It needs an offset

.toc {
  position: sticky;
  inset-block-start: 1rem;
}

Without top or the logical inset-block-start, there is no threshold for the browser to use. The element remains in normal flow.

Using logical inset properties keeps the rule tied to document flow. See logical properties for the broader model.

The scroll container matters

Any ancestor with overflow that creates a scroll container can change where sticky is evaluated. This is the classic failure:

.page {
  overflow: hidden;
}

That rule may have been added to hide a decorative bleed, but it can also trap sticky descendants. Prefer fixing the element that overflows instead of putting overflow: hidden on a large page wrapper.

.article-body {
  min-inline-size: 0;
}

.article-body pre {
  overflow-x: auto;
}

This keeps sticky behavior available while putting overflow responsibility on the code block that needs it. See intrinsic sizing for the sizing side of that debugging workflow.

Sticky needs space

If the sticky element is as tall as its container, it has nowhere to stick. A sidebar table of contents inside a short parent may appear broken because the parent ends before the sticky behavior becomes visible.

.article-layout {
  display: grid;
  grid-template-columns: minmax(0, 1fr) 16rem;
  align-items: start;
}

.article-toc {
  position: sticky;
  inset-block-start: 1rem;
}

align-items: start prevents the sidebar from stretching to match the main content height, which often makes sticky behavior easier to reason about.

Sticky elements do not stick forever. They are constrained by their containing block. A table of contents should not overlap the footer forever, but it needs to live inside a parent that spans the article content if it should stay active through the article.

Design around mobile

Sticky sidebars rarely belong on narrow screens. Use sticky for local, high-value controls such as an alphabet nav, a filter bar, or a table header only when it does not steal reading space.

@media (min-width: 56rem) {
  .article-toc {
    position: sticky;
    inset-block-start: 1rem;
    max-block-size: calc(100svh - 2rem);
    overflow: auto;
  }
}

Sticky is best used as a progressive enhancement. The page should remain readable when the element simply scrolls away.

The debugging move

When sticky fails, confirm the offset first. Then walk up the ancestors and inspect overflow, parent height, and Grid or Flexbox alignment. Temporarily outline the sticky element and its parent so you can see whether it has room to move.

Do not start by changing z-index. Layering only matters after sticky positioning works.

A sticky element is always visually present, so it should earn that space. Avoid sticky elements that cover content, trap focus, or create a moving target while users zoom.

Choose sticky targets carefully

Sticky works best for controls that remain useful while nearby content scrolls: section navigation in a long article, table headers in a data region, filter controls above a result list, or a small action bar tied to a form. It works poorly for large branding blocks, decorative panels, or content that competes with reading space.

If the sticky region can become taller than the viewport, give it its own maximum block size and internal scrolling. Then test keyboard focus inside that region. A sticky table of contents that traps the wheel or hides focus at the bottom of the viewport is not an improvement.

Account for other fixed or sticky surfaces. If the site has a sticky header, the sidebar offset should include that header space. Use a shared custom property for the header height if the relationship appears in several places. Otherwise, one sticky layer will quietly cover another at certain viewport sizes.

Sticky elements should not become a second navigation system competing with the main page. Keep their content focused on the local task: section links for the current article, table headers for the current table, or filters for the current result set. If the sticky region grows into a full sidebar application, it needs its own responsive design and accessibility review.

Related guides: Alignment in CSS, Intrinsic sizing, and Logical properties.

References