CSS Position Sticky Not Working: The 3 Reasons Nobody Mentions
You set position: sticky. You added top: 0. You saved. You refreshed. The element scrolled right off the page like the property meant nothing to it. So you went looking, and every answer said the same thing: make sure you added a top value. You did. It’s still not working.
This is what CSS failing silently feels like. No error. No crossed-out rule staring back at you. Just a property that appears to be applied, and a layout that ignores it completely. It’s one of the more maddening failures in CSS because the problem has nothing to do with the property itself and everything to do with something invisible above it in the tree.
I’ve seen this described in so many ways online, but one version stuck with me. Someone on Reddit put it this way: they felt like they’d tried everything, the element just wouldn’t stick, and they were starting to wonder if there was something fundamental they’d gotten wrong about how position: sticky actually works. That framing is exactly right. It’s not that they missed a step. It’s that the failure is in a place nobody told them to look.
Here are the 3 reasons position: sticky stops working, and what to do about each one.
Reason 1: An ancestor has overflow set to hidden, auto, or scroll
This is the one that gets almost everyone, and it’s the least obvious because the problem isn’t on the sticky element at all. It’s on a parent or grandparent somewhere up the DOM tree, possibly one you didn’t write and wouldn’t think to check.
When any ancestor has overflow: hidden, overflow: auto, or overflow: scroll, the browser treats that ancestor as its own scrolling container. Your sticky element now tries to stick within that container instead of the viewport. If the container doesn’t actually scroll, which is common when overflow: hidden was set just to stop horizontal bleed, the sticky element has nowhere to stick to. It behaves like it was never set.
The frustrating part: overflow: hidden gets added all the time by themes and page builders, usually to prevent horizontal scrolling on mobile. It’s set on a body wrapper, or a section element, or some container that has nothing to do with your sticky element. Someone set it for a completely different reason, and it quietly breaks sticky as a side effect. You didn’t add it. You can’t see it easily. And nothing about the failure tells you where to look.
This is exactly the thing that drives me past the limit with CSS on a site that has competing stylesheets from a page builder, plugins, and WordPress itself. A stranger on a forum can give you the right answer in theory. But they don’t know your specific stack, and the fix for a generic site and the fix for your site aren’t always the same thing.
To find it, open DevTools, click your sticky element, then walk up the DOM tree and check the computed overflow value on each parent. Look for anything that isn’t visible. That’s your culprit. If you can edit that CSS, change it to overflow: visible. If the rule came from a theme or plugin you can’t touch directly, try overflow: clip instead of overflow: hidden. Unlike hidden, clip doesn’t create a new scroll container, so it stops horizontal bleed without breaking sticky. If you’d rather not walk the ancestor chain manually, Loupely Lens clicks on the element and reads the full ancestor chain for you, showing exactly which parent has the overflow set and where that rule came from.
Reason 2: No threshold value, or the wrong one
Position: sticky requires at least one of top, bottom, left, or right to be set. Not because the browser is being difficult, but because sticky needs to know the exact point at which it should stop scrolling and start sticking. Without a threshold, it doesn’t have one, and it behaves like position: relative all the way down the page.
You probably set top: 0, which is the right instinct. But there are two ways this can still fail. First, another rule might be overriding it. A theme stylesheet, a page builder’s generated CSS, or an inline style applied somewhere can override your top value with something unexpected. Open DevTools and look at the computed value for top on your sticky element. If it shows auto, something overrode it. That’s a specificity conflict, which is a separate problem from the threshold itself. The rule that wins the cascade determines what actually applies, and finding it is the whole diagnostic.
Second, if your sticky element is inside a flexbox container where align-items is set to stretch (which is the default), the element stretches to match the full height of its flex container. When that happens, there’s no room for it to scroll within the parent, and sticky never activates. Setting align-self: flex-start on the sticky element usually fixes it.
Worth checking separately: does position: sticky even appear in the computed styles, or is it crossed out? If it’s crossed out, that’s a specificity conflict, not a threshold problem. They look the same from the outside but need different fixes.
Reason 3: The parent container is too short
This one is subtle. Sticky positioning doesn’t work relative to the whole page. It works relative to the sticky element’s parent container. The element sticks while it’s scrolling within that parent, and it unsticks when the parent scrolls out of view. If the parent container is roughly the same height as the sticky element itself, there’s no scrollable distance inside it for sticky to activate. The element has nowhere to go.
This shows up most often with sidebars. You have a sticky sidebar element inside a sidebar container, and the container is sized by its content rather than set to a specific height. If the sidebar content is short, or if the container was sized to match its children, the sticky element fills the whole parent height and never has room to move. Setting the parent to a minimum height that gives the sticky element real scrollable space usually fixes it.
The constraint hiding your layout is almost always a few levels up from where it looks broken. That’s true here too. The sticky element looks fine. The parent container looks fine. The failure lives in the relationship between them, in a height the browser calculated that you never explicitly set.
How to diagnose which reason it is
Start with the ancestor overflow check. Open DevTools, find your sticky element, and walk up the tree. For each parent, check the computed overflow value. If any of them show anything other than visible, start there. This is the most common cause, though I’d be skeptical of any specific number you read about how often. Those statistics tend to get repeated because they sound authoritative, not because anyone actually measured them.
If the ancestor tree is clean, check the threshold. Look at the computed styles on the sticky element itself. Confirm position: sticky is applied and not crossed out. Confirm top has a real value and isn’t being overridden to auto.
If both of those are fine, look at the parent height. Inspect the direct parent of your sticky element and check its rendered height. If it’s close to the height of the sticky element itself, you’ve found it.
The pattern is the same across most CSS failures: the problem isn’t where you’re looking. It’s above it, in something you didn’t touch, set for reasons that have nothing to do with what you were trying to do. Once you know which of the 3 reasons it is, the fix is usually one line of CSS. The diagnostic is the hard part. That gap between finding it and fixing it is where most of the time goes, and it’s the gap that tools like Lens exist to close.
