Skip to article body
A man standing still at the mast of a felucca while the Nile riverbank smears past in long streaks
A man standing still at the mast of a felucca while the Nile riverbank smears past in long streaks

Web Development · Field Notes

The Sticky Element That Scrolled Away

Why overflow: hidden on an ancestor cancels position: sticky, and the value that does not.

By Shehab Waleed, Founder, Cairo Studio 3 min read

We built a process section that pins while its steps change. The first version pinned with GSAP. When we moved it to CSS sticky, which is lighter and plays well with native scrolling on phones, the section still carried overflow: hidden from the GSAP version. With that one class in place, sticky would have done nothing at all.

Sticky sticks to a scroll container

A sticky element is positioned against its nearest ancestor that scrolls. CSS counts any element with overflow set to hidden, scroll or auto as one that scrolls, whether or not it has anything to scroll. Our section used overflow: hidden to stop wide content from spilling sideways.

.process {
overflow: hidden;
}

.process-stage {
position: sticky;
top: 0;
}

So the stage sticks to the section. The section never scrolls on its own, it moves with the page, and the stage moves with it. The result looks exactly like an element that ignores sticky.

clip hides overflow without the side effect

overflow: clip cuts off overflowing content just as hidden does, but it does not make the element a scroll container. We changed the section to clip in the same commit that added sticky, and the stage looks past the section to the page.

.process {
overflow: clip;
}

In Tailwind this is overflow-clip, or overflow-x-clip when only one axis should be cut. We tested both values with a sticky child in headless Chromium and in WebKit. With hidden on the parent, the sticky element had scrolled 1000 pixels out of view. With clip, it sat at the top of the viewport.

The body is a special case

We had overflow-x: hidden on the body too, and changed it to clip during the same week of scroll work. Our tests since then show that this one was probably not the cause. When the html element has the default overflow, the body hands its overflow to the viewport, and sticky keeps working in both engines.

It breaks when both html and body have a value other than visible. The body then keeps overflow for itself, becomes a scroll container, and every sticky element on the page sticks to it. That is easy to cause by accident, because scroll libraries and modal code often set overflow on html. We keep clip on the body because it is safe in every one of these cases.

Finding the ancestor

When sticky does nothing, walk up from the element and print every ancestor whose overflow is not visible.

let el = document.querySelector(".process-stage");
while ((el = el.parentElement)) {
const { overflowX, overflowY } = getComputedStyle(el);
if (overflowX !== "visible" || overflowY !== "visible")
console.log(el, overflowX, overflowY);
}

Values of clip are harmless. Anything else is a scroll container. It is the same kind of search we used when a transform that moved nothing trapped our fixed elements.

Share
X LinkedIn Facebook WhatsApp Email
A hand holding a coiled rope on the edge of a boat as the river streaks past

Held in place, relative to what it is tied to.

hidden
Yes
clip
Yes
hidden
Yes
clip
No
hidden
Stick to this element
clip
Stick to the next scroller up
hidden
Yes, with scrollTo
clip
No

The most common cause is an ancestor with overflow set to hidden, auto or scroll. The sticky element sticks inside that ancestor instead of the page. Other causes are a missing top value or a parent with no room to scroll.

Yes, in current versions of Chrome, Safari and Firefox. Older Safari versions before 16 do not support it, so check your audience if that matters.

Usually not, because the body passes its overflow to the viewport. It does when html also has overflow set, which some scroll and modal libraries do. overflow-x: clip avoids the question.

We design and build fast, animated sites, and we test them on real phones before they ship.

See our web development work

More from the Journal

view all →