How to Create CSS Animations on Scroll [With Examples]

Oscar Jite Avatar

CSS Scroll animations are a great way to bring boring and static sites to life and give the reader a more interesting, unique, and modern experience.

In this post, you will learn how to trigger CSS animations on scroll.

And… who knows? Maybe you will end up creating unique websites like these scroll-animated websites.

What are CSS Scroll Animations?

CSS scroll animations are animations that get triggered using CSS when the user scrolls through a website. Usually, the scroll-driven animation is triggered when the element comes into view, and it can be applied to practically any element, such as text, images, and videos.

Our eyes are naturally attracted to movement so this feature will entice and keep the visitor engaged. Adding eye-catching animations to your website will make it stand out from the rest, and when used properly, scroll animations can add a modern touch to a website.

Any CSS Scroll animation usually involves using a plugin or library, but we will show you how to achieve this without those. And we will do it in two ways, one using new experimental CSS features and in the traditional way that involves using plain JavaScript.

So, let’s get to it.

Scroll-based Animations with Pure CSS

Traditionally, scroll-driven animations using only CSS were not possible. However, there are now new CSS experimental properties that allow us to do so.

If you prefer not to rely on external libraries or JavaScript, you can still trigger CSS-based animations on scroll by using the CSS property animation-timeline and the scroll() function as its value.

Note the CSS feature “animation-timeline: scroll()” is still not fully supported by all web browsers like Safari or Firefox. If you need to provide support for all browsers I recommend using this polyfill.

Let’s create a simple example where we will show a scroll indicator at the top of the page. This indicator will become bigger in size the more we scroll until reaching the bottom.

1. Create the HTML Markup

<div class="scroll-watcher"></div>

<div class="box">Scroll</div>
<div class="box">Scroll</div>
<div class="box">Scroll</div>
<div class="box">Scroll</div>
<div class="box">Scroll</div>Code language: HTML, XML (xml)

We create the scroll-watcher element that will animate on scroll. Then, to make the page scrollable, we added some “dummy” elements with the class box .

2. Styling the Elements

We will be adding our scroll indicator on the very top as a horizontal line that increases in width while we scroll down. Then, the dummy boxes force the page to be scrollable.

.scroll-watcher {
  height: 10px;
  position: fixed;
  top: 0;
  left:0;
  z-index: 1000;
  background-color: red;
  width: 100%;
}

.box {
  height: 60vh;
  padding: 30px 0 0 0;
  text-align: center;
  background: #4e4e4e;
  width: 400px;
  margin: 30px auto;
  border-radius: 12px;
  font-size: 1.4em;
  color: white;
}
Code language: CSS (css)

3. Creating the Animation on Scroll

Now, we will add the magic that will allow us to trigger animations on scroll. We will be adding these styles to the .scroll-watcher element:

  /* Initial X scale will be 0, Y will be 1*/
  scale: 0 1;

  transform-origin: left;
  
  /* Defining the animation and its easing function */
  animation: scroll-watcher linear;

  /* Adding the scroll trigger */
  animation-timeline: scroll(y);Code language: CSS (css)

And the scroll-wather animation that will define how we want the element to animate; in this case, the element will scale the X property to its maximum value once the scroll reaches the end:

@keyframes scroll-watcher {
  to {
    scale: 1 1;
  }
}Code language: CSS (css)

4. Here’s our final CSS

.scroll-watcher {
  height: 10px;
  position: fixed;
  top: 0;
  left:0;
  z-index: 1000;
  background-color: red;
  width: 100%;
  scale: 0 1;
  transform-origin: left;
  animation: scroll-watcher linear;
  animation-timeline: scroll(y);
}

@keyframes scroll-watcher {
  to {
    scale: 1 1;
  }
}

.box {
  height: 60vh;
  padding: 30px 0 0 0;
  text-align: center;
  background: #4e4e4e;
  width: 400px;
  margin: 30px auto;
  border-radius: 12px;
  font-size: 1.4em;
  color: white;
}Code language: CSS (css)

Trigger In-view CSS Animations On Scroll

What if you want to trigger an animation when the element enters the viewport instead of based on the scroll position?

This can also be done using the same animation-timeline property but in this case using the function view() as its value.

Note the CSS feature “animation-timeline: scroll()” is still not fully supported by all web browsers like Safari or Firefox. If you need to provide support for all browsers I recommend using this polyfill.

It’s quite common to see websites showing text elements fading in or slightly animating into the viewport when scrolling the page. Traditionally, this was done using JavaScript, but now we can replicate some of those effects using just CSS.

Let’s create a simple example where we are fading images once they enter into view.

1. Setting up the HTML

We will be adding some elements to make the page scrollable like before, then the images in between:

<div class="box">Scroll</div>
...
<img src="https://example.com/image.jpg" />
<div class="box">Scroll</div>
...
<img src="https://example.com/image.jpg" />
<div class="box">Scroll</div>
...Code language: HTML, XML (xml)

2. Adding the CSS

In this case, we will be wrapping the code in a media query to make sure that it only applies when the user preference allows motion. Otherwise images would never become visible without the animation.

We then set the initial opacity of the image to 0, the scale to 0.8, and add the animation-timeline: view(); property.

Note we are also using the animation-range property. In this case, we are using it to tell at what point in the viewport we want the images to start animating and at what point we want the animation to finish.

In this specific case, we want images to start animating when they are 50px away from the bottom of the viewport. We also want them to finish the animation when they are 36% away from the bottom of the viewport.

@media (prefers-reduced-motion: no-preference){
  img{
    opacity: 0;
    scale: 0.8;
    animation: fade-in linear forwards;
    animation-timeline: view();
    animation-range: 50px 36%;
  }

  @keyframes fade-in {
    to {
      opacity: 1;
      scale: 1;
    }
  }
}

.box {
  height: 60vh;
  padding: 30px 0 0 0;
  text-align: center;
  background: #4e4e4e;
  width: 400px;
  margin: 30px auto;
  border-radius: 12px;
  font-size: 1.4em;
  color: white;
}Code language: CSS (css)

Something’s not clear? Then this video can come in handy for you:

Scrolling Animation with Vanilla JavaScript and CSS

Vanilla JavaScript, despite its fancy name, is not a library, it is just plain old JavaScript. So don’t get confused by the “fancy” name.

1. Setup the Page

First things first, create a web page. Just a simple layout with multiple sections.

<section class="container">
  <h2>Caption</h2>
  <div class="text-container">
    <div class="text-box">
      <h3>Section Text</h3>
      <p>Random text</p>
    </div>
    <div class="text-box">
      <h3>Section Text</h3>
      <p>Random text</p>
    </div>
    <div class="text-box">
      <h3>Section Text</h3>
      <p>Random text</p>
    </div>
  </div>
</section>
Code language: HTML, XML (xml)

2. Styling the Page with CSS

Add style attributes to your page and use CSS to define the scroll animation style, Now you need the class, reveal for the sections you are animating and a new class name, active, for when it’s activated.

.reveal{
  position: relative;
  transform: translateY(150px);
  opacity: 0;
  transition: 2s all ease;
}
.reveal.active{
  transform: translateY(0);
  opacity: 1;
}
Code language: CSS (css)

With this, the reveal elements will be hidden until the active class is added. The transform and transition attributes define the scroll animation style, with this, the sections will fade in from the bottom and move, along the y-axis, towards the top. You can check this CSS Transition guide if you have any doubts.

3. Create JavaScript Functions to Target the Elements

We will need these functions to assign the new class name when they enter the viewport and we need it to trigger CSS animations on scroll.

Start by targeting all the reveal elements using document.querySelectorAll().

function reveal() {
  var reveals = document.querySelectorAll(".reveal")
Code language: JavaScript (javascript)

The scrolling animation needs to be triggered when it comes into view so we need to determine the element’s position on the page, that is, the distance of the element from the top of the viewport.

getBoundingClientRect().top gives us this distance from the top of the viewport and window.innerHeight will give us the height of the viewport.

With this, we can set the conditions using for;

for (var i = 0; i < reveals.length; i++) {
  var windowHeight = window.innerHeight;
  var elementTop = reveals[i].getBoundingClientRect().top;
  var elementVisible = 150;
}Code language: JavaScript (javascript)

The variable, windowHeight is the height of the viewport, elementTop is the distance of the reveal element from the top of the viewpoint or, the length that has been scrolled, and elementVisible is the height at which the element should be revealed to the user.

You can determine when an element has scrolled a certain number of pixels into the page. Now define a function that displays the elements by adding and removing the active class. For this. use if and else statements. These will set the conditions for triggering the animation

if (elementTop < windowHeight - elementVisible) {
  reveals[i].classList.add("active");
} else {
  reveals[i].classList.remove("active");
}Code language: JavaScript (javascript)

The complete function will look like this;

function reveal() {
  var reveals = document.querySelectorAll(".reveal");
  for (var i = 0; i < reveals.length; i++) {
    var windowHeight = window.innerHeight;
    var elementTop = reveals[i].getBoundingClientRect().top;
    var elementVisible = 150;
    if (elementTop < windowHeight - elementVisible) {
      reveals[i].classList.add("active");
    } else {
      reveals[i].classList.remove("active");
    }
  }
}

Code language: JavaScript (javascript)

Now we just pass it into an event listener to run it every time the visitor scrolls the page in any direction.

window.addEventListener("scroll", reveal, {passive: true});

// To check the scroll position on page load
reveal();
Code language: JavaScript (javascript)

That’s it! You have achieved CSS scroll animation.

But, what if you want more? Right now, every section has the same uniform animation. Let’s give each section a different scroll animation style.

4. Animate with CSS

First, we will assign classes in the HTML so we can reference them later on in our CSS to create the animations we want.

Here’s how we would do it in our first section:

<section class="container">
    <h2>Caption</h2>
    <div class="text-container reveal fade-bottom">
        <div class="text-box">
            <h3>Section Text</h3>
            <p>Random text</p>
        </div>
        <div class="text-box">
            <h3>Section Text</h3>
            <p>Random text</p>
        </div>
        <div class="text-box">
            <h3>Section Text</h3>
            <p>Random text</p>
        </div>
    </div>
</section>
Code language: HTML, XML (xml)

And the same applies to any other sections we have in our HTML.

Then, in our CSS we can assign them different animations like so:

.reveal {
  position: relative;
  opacity: 0;
}
.reveal.active {
  opacity: 1;
}
.active.fade-bottom {
  animation: fade-bottom 1s ease-in;
}
.active.fade-left {
  animation: fade-left 1s ease-in;
}
.active.fade-right {
  animation: fade-right 1s ease-in;
}Code language: CSS (css)

Define the animations using Keyframes. The transform style attribute can bring a dynamic feel to your page and it can be added here. Another attribute you can add is transition, which is the time it takes for the element to load completely, but the 1s in the animation attribute achieve the same result.

@keyframes fade-bottom {
  0% {
    transform: translateY(50px);
    opacity: 0;
  }
  100% {
    transform: translateY(0);
    opacity: 1;
  }
}
@keyframes fade-left {
  0% {
    transform: translateX(-100px);
    opacity: 0;
  }
  100% {
    transform: translateX(0);
    opacity: 1;
  }
}
@keyframes fade-right {
  0% {
    transform: translateX(100px);
    opacity: 0;
  }
  100% {
    transform: translateX(0);
    opacity: 1;
  }
}Code language: CSS (css)

You can change the transform values and animation attributes to achieve more variety.

5. Final Result

You could animate individual blocks of text, let each paragraph load one after the other. Just assign the class name to whatever you want, style and animate with CSS, sprinkle a little vanilla Js and watch the magic.

Animating the Navigation Bar on Scroll

To add another practical example of using animations on scroll, we will animate a navigation bar based on the scroll position of the page.

We can use our previous example and add a navigation bar to it. Let’s see how to do it.

Feel free to check out how to create a sticky or fixed navbar if you want to get into more details.

1. Setup the NavBar to animate

First, the HTML:

<nav>
  <a href="#home" class="active">Home</a>
  <a href="#about">About</a>
  <a href="#services">Services</a>
  <a href="#contact">Contact</a>
</nav>
Code language: HTML, XML (xml)

2. Style the NavBar With CSS

a {
  text-decoration: none;
}
ul {
  list-style: none;
}
header {
  position: fixed;
  top: 0;
  left: 0;
  width: 100%;
  z-index: 1000;
  background: #42455a;
  padding: 20px;
  text-align: center;
  border-bottom: 1px solid #00c2cb;
}
header nav a {
  padding: 10px 20px;
  font-size: 2rem;
  color: #e0ffff;
  border-radius: 5px;
  letter-spacing: 0.5px;
}
Code language: CSS (css)

Then you add the link styling for when the animation becomes active, you can combine this with the hover style, it’s your choice;

header nav a:hover,
header nav a.active {
  background: #00c2cb;
  color: #42455a;
  transition: 0.5s ease-out;
  letter-spacing: 2px;
}
Code language: CSS (css)

3. Using JavaScript to Animate Our Navigation Bar

First target all the sections and links;

let section = document.querySelectorAll('section');
let menu = document.querySelectorAll('header nav a');
Code language: JavaScript (javascript)

Just like the first example, you want the navbar to be animated when the section is scrolled into view, for that, create an onscroll event and use forEach to call the function. For this, we need to declare the parameters and set the condition.

window.onscroll = () => {
  section.forEach(i => {
    let top = window.scrollY;
    let offset = i.offsetTop - 150;
    let height = i.offsetHeight;
    let id = i.getAttribute('id');
Code language: JavaScript (javascript)

top is the variable for scrollY, which is the length or number of pixels the viewport has been scrolled. offsetTop is the length of the element from the top of the viewport. offsetHeight is the length of the sections and getAttribute() returns the value of an elements attributes, in this case, the id of the sections.

Next, the conditions for execution so the links become active as you scroll down the page. The section should be inside the viewport so, the offset should be less than the length you scroll and also, the length from the top of the viewport and the length of the section or element should be more than the length you have scrolled;

if (top >= offset && top < offset + height) {
  menu.forEach(link => {
  	link.classList.remove('active');
    document.querySelector('header nav a[href*=' + id + ']').classList.add('active');

Code language: JavaScript (javascript)

So now, when you scroll into a new section, the condition is met and the active attributes will be moved to the next link.

Put it all together and it looks like this;

let section = document.querySelectorAll('section');
let menu = document.querySelectorAll('header nav a');
window.onscroll = () => {
  section.forEach(i => {
    let top = window.scrollY;
    let offset = i.offsetTop - 150;
    let height = i.offsetHeight;
    let id = i.getAttribute('id');
    if (top >= offset && top < offset + height) {
      menu.forEach(link => {
        link.classList.remove('active');
        document.querySelector('header nav a[href*=' + id + ']')
          .classList.add('active');
      });
    }
  });
};
Code language: JavaScript (javascript)

4. Result: CSS Scroll Animation + NavBar!

You can add the smooth scroll-behavior attribute to give your website a truly dynamic feel. Make tweaks to the animation and transform style attributes and see what happens. Add a transition-delay to make it even more dramatic (check how to configure this attribute in this CSS Transition guide)

Conclusion

The scroll animation effect is a popular animation in todays websites and provide them with a modern and more dynamic look.

There are many other ways to create animations on scroll such as using components like fullPage.js that will combine animations and scroll in a beautiful way. Fullpage works by snapping full-screen sections into view when the visitor scrolls creating quite a unique and interesting user experience.

And there are many other cool animations you can use on scroll. Just choose the one that fits your needs and creates the best scrolling experience for your visitors and page.

Was this page helpful?