Scroll-Driven Animations: You want overflow: clip, not overflow: hidden

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!

<div id="scroller" style="overflow: scroll;">
     <div id="intermediate" style="overflow: hidden">
        <div id="target" style="animation-timeline: scroll()">
            …
        </div>
    </div>
</div>
❌ Problematic code example

~

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.

<div id="scroller" style="overflow: scroll;">
     <div id="intermediate" style="overflow: clip">
        <div id="target" style="animation-timeline: scroll()">
            …
        </div>
    </div>
</div>
✅ Fixed code example

☝️ 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:

Screenshot of overflow: clip support on CanIUse

~

# Spread the word

To help spread the contents of this post, feel free to retweet the announcements made on social media:

~

Published by Bramus!

Bramus is a frontend web developer from Belgium, working as a Chrome Developer Relations Engineer at Google. From the moment he discovered view-source at the age of 14 (way back in 1997), he fell in love with the web and has been tinkering with it ever since (more …)

Unless noted otherwise, the contents of this post are licensed under the Creative Commons Attribution 4.0 License and code samples are licensed under the MIT License

Join the Conversation

1 Comment

Leave a comment

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.