No Prequisites

The code below does not require any libraries or frameworks. It is just plain HTML, CSS, and JavaScript. So for this effect, I am using the browser’s built-in Intersection Observer API. Nothing else.

How It Works

The JavaScript code below will add opacity-0 class to all elements with the observe class which are not in the view right now. Then it is passively waiting for the element to be 25% in view. When that happens, it will remove the opacity-0 class and add the animate-slide-in class to the element. This will trigger the CSS animation.

Unserstanding threshold and rootMargin

When working with the Intersection Observer API, you will come across two important properties: threshold and rootMargin. Here is a nice interactive resource to understand these properties better: Intersection Observer Playground

HTML

For this effect to work, all we need to do is to add the observe class to the element where we want this effect to happen.

<div class="observe">
  This should fade in when 25% of
  this element is in view, 
  creating a nice fade in effect.
</div>

Of course you will also need to add the CSS and JavaScript below for this magic to work.

CSS

I am using Tailwind but if you don’t use it, here is the CSS you will need for this effect:

@keyframes slide-in {
  0% {
    transform-origin: top;
    transform: scale(0.8);
    opacity: 0;
  }
  100% {
    transform: scale(1);
    transform: translateY(0);
    opacity: 1;
  }
}
 
.animate-slide-in {
  animation: slide-in 0.5s ease-out;
}
 
.opacity-0 {
    opacity: 0;
}

JavaScript

At first we will create an observer and then call .observe(section) on all elements with the observe class.

const callback = (observerEntries) => {
  observerEntries.forEach((entry) => {
    if (entry.isIntersecting) {
    entry.target.classList.remove('opacity-0');
    entry.target.classList.add('animate-slide-in');
    } else {
    entry.target.classList.add('opacity-0');
    }
  });
}
const options = {
  rootMargin: '-0% 0% -25% 0%'
};
const observer = 
  new IntersectionObserver(callback, options);
 
document
  .querySelectorAll('.observe')
  .forEach((section) => {
    observer.observe(section);
  });

Warning

If an element is not at least 25% in the view it will stay invisible.