DEV Community

Akash Shyam
Akash Shyam

Posted on

Scroll Based Animations with ZERO Javascript 🔥🔥

At some point of time, most of us have used GSAP, Javascript IntersectionObserver or jQuery(c'mon guys, its 2021... using jQuery instead of javascript is like using IE for web development), in the foreseeable future, this can be done using CSS!

No, it's not a typo and I'm not insane. It's possible using the (drumroll please...) @scroll-timeline. However, everything is not fine and dandy, this is still in draft and is supported only by Chrome 89(after turning on an experimental flag).

The Scroll-linked Animations is a new addition to CSS that defines a way for creating animations that are linked to a scroll offset of a scroll container. The specification is still in draft, and in no way finalised nor official, it already has experimental support in Chromium.

In sometime, maybe in a year or so, all major browsers will support it(who cares about Internet Explorer anyway? Even Microsoft ditched it).


I'll be using codepen to demonstrate the animations. Let's start by enabling the expiremental feature. Go to chrome://flags and enable "Experimental Web Platform features".

Add some basic HTML -

    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=">

    <p>scroll down</p>

<div class="container">
    <div class="inner">
        <h1>Scroll based animations in CSS is so coooool</h1>
        <img src="" alt="placeholder image" class="image" id="image">

Enter fullscreen mode Exit fullscreen mode

Nothing too complicated here.... just a basic outline for the body. Inside that, I have a p and 2 divs with a h1 and img. There's a section with the height of 100vh below to give some scrolling space.

I'll use Sass for this tutorial but this can be done in CSS too. I'll add a link for the CSS version at the end of the post. I'm going to add some quick styling that is not at all related to scroll based animations.

// Unimportant and boring styling

body {
    background-color: #ffd9d1;

section {
    height: 100vh;

p {
    font-size: 1.5em;
    display: grid;
    text-transform: uppercase;
    letter-spacing: 1em;
    place-items: center;
    margin-top: 100px;

.inner {
    width: 60%;

.container {
    text-align: center;
    min-height: 80vh;
    display: grid;
    place-items: center;

    h1 {
        font-size: 3em;
        font-family: Poppins;

    .image {
        height: 400px;
        background-size: cover;
        margin-top: 3em;
Enter fullscreen mode Exit fullscreen mode

I've just added some position, dimensions, fonts.... you get the idea. Not at all related to this demo. Let's get to the crux of the code. We'll create a very simple pulse keyframe animation that works with the scale of the image.

@keyframes demo {
    0% {
        border: 20px solid yellow;

    25% {
        border: 20px solid green;

    50% {
        border: 20px solid blue;

    100% {
        border: 20px solid red;
Enter fullscreen mode Exit fullscreen mode

Nothing too complicated here.... just changing the border at various stages of the animation. I've chosen to go with borders because every time you change the position of the scrollbar the color changes and it's helpful to debug(I know its not good to animate border but I don't want to complicate things). Next, we have to create a timeline.

@scroll-timeline timeline {
    time-range: 1s;
Enter fullscreen mode Exit fullscreen mode

We use the @scroll-timeline rule to make a timeline. I've called it timeline. Inside this, I've specified a time-range i.e. the amount of time I want to run the animation for. As we have defined our animation-duration to be 1s, we want our time-range to reflect that same duration. So, Scrolling from top to bottom (e.g. from 0% to 100%) should advance the animation by 1s.

The time-range property is not the time of a clock. It is a mapping. This property is one of the most important properties in a @scroll-timeline rule. Let's make sure you understand this well by looking at some examples.

  • animation-duration & time-range is 1 second
    As I said, time-range is a mapping. So, 0% of our keyframe maps to 0 seconds and 100% maps to 1 second. In this case, the animation will go from start to finish as the user scrolls from top to bottom

  • animation-duration is 1s & time-range is 2s
    This time around, 0% of our keyframe maps to 0 seconds and 100% maps to 2 second.

So, the animation will go twice as fast.

Properties of a Timeline

A scroll timeline has some more properties -

  1. source
  2. orientation
  3. scroll-offsets

We'll look at these as we go through the tutorial.

Linking the Bits and Pieces

We need to link our scroll-timeline, keyframe animation with our object. Add the following properties to the object we want to animate.

#image {
    animation: 1s linear forwards demo;
    animation-timeline: pulse-timeline;
Enter fullscreen mode Exit fullscreen mode

The first line is nothing new, just setting an animation for the object for it to repeat once for 1 second. The second line is where it gets interesting. I've specified an animation-timeline that links the timeline we made earlier.

From this, we can deduce that the three components for scroll based animations are:

  1. An animation(eg keyframe)
  2. A scroll timeline(eg pulse-timeline we just made)
  3. The link(Linking animation with timeline)

Scroll Offsets

If you run the code, the animation continues even after the object is out of the viewport. This is where the scroll-offsets property comes into the picture. The scroll-property takes 2 values i.e. the offset for the starting point and the offset for the end.

@scroll-timeline pulse-timeline {
    time-range: 1s;
    scroll-offsets: 0%, 100vh;
Enter fullscreen mode Exit fullscreen mode

I haven't fiddled with the starting value as that works for us however I set the second value to 100vh which means that it ends at 100vh. If you notice, the animation speed has also slowed down that shows the mapping ratio has decreased.

Scroll Orientation

By default a scroll-timeline will scroll from top to bottom i.e. vertical orientation. If we specify an orientation of horizontal, the animation will work during horizontal scrolling.


The source is scrollable element or container(default is body) whose scrolling triggers the activation of the animation and changes the timeline.

The full demo


Normally for scroll linked animations I'd use GSAP/Intersection Observer along with ScrollMagic. Some parts of GSAP are not free and I feel that scroll magic has big learning curve(or the documentation isn't great). These also increase bundle size.

It's great to see scroll based animations in CSS and I'm pretty certain this is going to be the way to go(once there's better support in browsers).

That's it for now, hope you liked this post. If you did please like this post and follow me. Bye 👋

Discussion (0)