overflow: hidden breaks position: sticky. overflow: clip doesn't.
A sticky header that worked in isolation stops sticking the moment it's dropped inside a rounded-corner card with overflow: hidden. The card isn't scrollable — but the browser doesn't care. overflow: clip is the fix, and it's been quietly ready for a while.

Someone reports a bug: the "jump to section" bar at the top of a long settings page used to stick to the top of the viewport while you scrolled. Now it doesn't. It just sits there in normal flow like position: sticky was never applied.
You open devtools. The CSS is untouched — position: sticky; top: 0;, same as it's been for months. No typo, no overridden rule, nothing crossed out in the inspector. The element is exactly as sticky as it was yesterday. Except it isn't sticking.
The actual change shipped in a different file entirely: a shared <Card> component picked up overflow: hidden last week, to stop a decorative background image from spilling past its rounded corners. Nobody touched the settings page. Nobody touched the sticky bar. And yet here we are.
position: sticky doesn't stick to the viewport by default. It sticks to its nearest scroll container — the closest ancestor whose overflow isn't visible. Usually that's the page itself, so sticky behaves exactly like you'd expect: pinned to the window as you scroll.
But "nearest ancestor with non-visible overflow" doesn't check whether that ancestor actually scrolls. It just checks the computed overflow value. Set overflow: hidden anywhere between the sticky element and the page — for any reason, clipping an image, trimming a shadow, stopping an accidental horizontal scrollbar — and that element becomes the sticky container instead of the viewport.
.card never scrolls — its content fits. So the sticky bar's "scroll container" never moves, which means the bar never has a reason to detach from its normal spot and pin anywhere. Visually: it just looks broken. There's no console warning, no deprecation notice, nothing that points back to a border-radius fix in an unrelated component.
Try it before you keep reading: open your own devtools on any sticky element that's misbehaving and check getComputedStyle on each ancestor for overflow. It's almost always hidden, and it's almost always there for a reason that has nothing to do with scrolling.
overflow: clip looks, on paper, like a quieter synonym for hidden. It clips content at the element's padding edge the same way. The difference is in what it refuses to become: the CSS Overflow spec defines clip so that it never creates a scroll container. There's no scrollport to establish, so nothing can be scrolled into it — not by the user, not by element.scrollTop = 40 in your own code. hidden still lets that second one through, which is exactly the mechanism it uses to qualify as scrollable in the first place.
Swap the one keyword and the sticky bar starts looking straight past .card again, up to the real scroll container — the page. Nothing else in the component changes. No wrapper <div>, no position: relative hack, no JavaScript recalculating offsets on scroll.
Two details worth knowing before you reach for it everywhere:
- It's independent per axis.
overflow-x: clip; overflow-y: visible;does what you'd want. Try that withhiddenand the browser forces the other axis toautofor you, which is its own long-running source of surprise scrollbars. overflow-clip-marginlets clipped content bleed a few pixels past the box before it's cut — useful when a focus ring or a shadow would otherwise get sheared off exactly at the edge.
The one thing overflow: clip can't do that hidden can is be scrolled at all — if that container is genuinely meant to scroll its own content (a chat panel, a code block with a max-height), hidden paired with a real scrollbar is still the right tool. clip is for the much more common case: an element that clips for layout reasons and was never supposed to be a scroll container in the first place.
Play with the before/after below — flip .card between hidden and clip and watch the sticky bar inside it react in real time.
Runs right in your browser — poke at it and watch the concept react live.
overflow: hidden was never just "clip this." It was always "clip this, and also: this now scrolls, even if you never intended that and nothing inside it ever moves." Every position: sticky bug report that starts with "it worked until I touched a totally unrelated component" is worth checking against exactly that rule before you touch the sticky declaration at all.
overflow: clip separates the two meanings that hidden always bundled together. If you only wanted the first one, it's the one to reach for now.
Next time a sticky element goes dead for no visible reason, what's the first ancestor you check — and how far up the tree did the real culprit turn out to be last time?
Think it clicked? Take the 7-question quiz →
Instant feedback, a hint on every question, and an explanation for each answer — right or wrong.
🚀 Want more like this? Every guide, playground, and quiz lives on bestpractic.org — open it and sign up free so the next one finds you.
Thanks for reading! Let's stay connected:
- ⭐ GitHub — follow me and star the projects: github.com/parsajiravand
- 💬 Discord — join the frontend best-practices community: discord.gg/d9KRhuAwQ
- 📸 Instagram — frontend best practices, daily: @bestpractice___
Keep reading
One post a day, in your inbox
Each one with a runnable playground and a quiz. No pitch, no digest, unsubscribe in one click.
0 comments
Sign in to join the discussion, like comments, and save articles for later.