Because overflow: hidden
creates a scroll container it can interfere with the Scroll-Driven Animations scroller lookup mechanism. The fix is to use overflow: clip
instead.
~
Scroll-Driven Animations
At its core, creating a Scroll-Driven Animation is pretty easy. Take an existing CSS Animation or WAAPI Animation, connect it to a ScrollTimeline or ViewTimeline, and you’re done.
In CSS, you do this using the animation-timeline
property:
#target {
animation: fade linear forward;
animation-timeline: scroll();
}
The scroll()
function – or scroll(nearest)
if you want to be more explicit about it – will look up the nearest ancestor scroller, and use its scroll position that to drive the animation.
Most of the times that works as expected, but sometimes it doesn’t do anything. The culprit: an overflow: hidden
sitting somewhere in between the target and the scroller.
👨🏫 New to learn more about Scroll-Driven Animations and want to learn more? Go check out scroll-driven-animations.style to learn all about it.
~
The problem with overflow: hidden
The overflow
property defines what should happen when content doesn’t fit in the parent element box. Its initial value is visible
, which forms the source material for the “CSS is awesome” meme:
To visually hide all the content that overflows, you can set overflow
to hidden
. But! Doing that comes with a caveat: when you set overflow: hidden
, it also creates a scroll container.
As per spec:
This value indicates that the box’s content is clipped to its padding box and that the UA must not provide any scrolling user interface to view the content outside the clipping region, nor allow scrolling by direct intervention of the user […]. However, the content must still be scrollable programmatically […] and the box is therefore still a scroll container.
The creation of a scroll container is what trips up the scroll(nearest)
lookup mechanism. In the snippet below scroll()
won’t find the #scroller
but the #intermediate
element with the overflow: hidden
applied to it instead. Uhoh!
~
overflow: clip
to the rescue!
You could try and work your way out of the problem caused by overflow: hidden
by refactoring your code to use a named Scroll Timeline. Honestly though, that’s a lot of work.
Thankfully there is alternative solution which is much more more easy: use overflow: clip
. overflow: clip
works exactly the same as overflow: hidden
, except for the fact that overflow: clip
does not create a scroll container.
As per spec:
[…] unlike
overflow: hidden
which still allows programmatic scrolling,overflow: clip
forbids scrolling entirely, through any mechanism, and therefore the box is not a scroll container.
And with that, the example below will work as initially expected: the #target
element will use the #scroller
element’s scroller to drive the animation.
☝️ Additionally, using overflow: clip
also allows you to use overflow-clip-margin
to determine how far outside its bounds an element may be painted.
~
Demo
Embedded below is a demo that demonstrates all this. On the left is a (red) box inside a wrapper that has overflow: hidden
applied to it. On the right the wrapper around the (green) box has overflow: clip
applied to it.
The (red) box on the left is not able to find the root scroller to drive its animation, but the (green) box on the right does.
See the Pen overflow: clip; ❤️ Scroll-Driven Animations by Bramus (@bramus) on CodePen.
~
In closing
I see no reason to use overflow: hidden
at all on your web pages, unless you have a good reason to. overflow: clip
has exactly the same outcome, doesn’t interfere with Scroll-Driven Animations, and has great browser support:
~
# Spread the word
To help spread the contents of this post, feel free to retweet the announcements made on social media:
Scroll-Driven Animations: You want overflow: clip, not overflow: hidden
> If you create a scroll-driven animation and find that `scroll(nearest)` is not working, check for `overflow: hidden` elements up the DOM tree and change them to `overflow: clip`.https://t.co/mzmt1NAUON pic.twitter.com/1vQrD56Z5O
— Bram.us (by @bramus) (@bramusblog) February 14, 2024
~
🔥 Like what you see? Want to stay in the loop? Here's how:
Hi Bram, good tip on using overflow: clip instead of overflow: hidden for scroll-driven animations. It’s a simple change that can save a lot of headaches. Definitely something I’ll keep in mind for future projects.
Stefan