Creating a Scroll-Triggered Reveal Without GSAP
Learn how to use vanilla JavaScript Intersection Observers alongside Velluma's CSS exports to create highly optimised scroll animations.
Read ArticleCreating a Scroll-Triggered Reveal Without GSAP
When building interactive websites, scroll-triggered animations are essential for creating a polished, engaging experience. For years, the default solution has been to drop a heavy library like GSAP into the project. While GSAP is incredibly powerful, it is often overkill for simple reveals and adds unnecessary weight to your site.
I prefer a cleaner approach. You can achieve beautiful, high-performance scroll reveals using pure CSS and the native JavaScript IntersectionObserver API. This method requires zero dependencies and keeps your codebase incredibly fast.
The CSS Setup
First, we need to set up the initial state of the elements we want to animate. We want them to start slightly lower on the page and be completely transparent. When the animation triggers, they will float up and fade in.
/* The starting state for our elements */
.reveal-item {
opacity: 0;
transform: translateY(30px);
transition: all 0.8s cubic-bezier(0.34, 1.56, 0.64, 1);
}
/* The active state when they scroll into view */
.reveal-item.is-visible {
opacity: 1;
transform: translateY(0);
}
Notice the cubic-bezier transition. This gives the animation a slight, natural bounce, making it feel much more premium and handcrafted than a standard linear ease.
The JavaScript Logic
Instead of tracking the scroll position manually—which is terrible for performance—we use IntersectionObserver. This native API quietly watches your elements and fires an event only when they enter the viewport.
// Select all elements you want to animate
const revealElements = document.querySelectorAll('.reveal-item');
// Set up the observer options
const observerOptions = {
root: null,
rootMargin: '0px',
threshold: 0.15 // Triggers when 15% of the element is visible
};
// Create the observer
const revealObserver = new IntersectionObserver((entries, observer) => {
entries.forEach(entry => {
if (entry.isIntersecting) {
// Add the visible class to trigger the CSS transition
entry.target.classList.add('is-visible');
// Stop observing once the animation has fired
observer.unobserve(entry.target);
}
});
}, observerOptions);
// Attach the observer to each element
revealElements.forEach(element => {
revealObserver.observe(element);
});
Why This Method Wins
By combining native CSS transitions exported from Velluma with a simple JavaScript observer, you bypass the need for external libraries entirely. The browser handles the animation natively on the GPU, resulting in a buttery smooth 60fps experience that will not negatively impact your load times or SEO.