DEV Community

Cover image for Advanced Scroll Direction-Aware Animations in Tailwind CSS (No Libraries Needed)
HexShift
HexShift

Posted on

Advanced Scroll Direction-Aware Animations in Tailwind CSS (No Libraries Needed)

Most scroll-triggered animations treat upward and downward scrolls the same — but what if you could animate *differently* based on scroll direction? This technique gives your UI a polished, professional feel without needing heavy libraries. Let's go beyond basic IntersectionObserver and build scroll direction-aware animations using Tailwind CSS and native JavaScript.

Why Care About Scroll Direction?

Reacting to scroll direction enables:

  • Elements that slide up when scrolling up, and fade down when scrolling down
  • More natural "reveal" animations that match user intent
  • Advanced UX patterns like smart sticky headers or timeline reveals

Step 1: Extend Tailwind for More Animation Variants

We'll define upward and downward animations separately:

// tailwind.config.js
module.exports = {
  theme: {
    extend: {
      keyframes: {
        fadeSlideUp: {
          '0%': { opacity: 0, transform: 'translateY(20px)' },
          '100%': { opacity: 1, transform: 'translateY(0)' },
        },
        fadeSlideDown: {
          '0%': { opacity: 0, transform: 'translateY(-20px)' },
          '100%': { opacity: 1, transform: 'translateY(0)' },
        },
      },
      animation: {
        fadeSlideUp: 'fadeSlideUp 0.8s ease-out forwards',
        fadeSlideDown: 'fadeSlideDown 0.8s ease-out forwards',
      },
    },
  },
}

Step 2: Markup with Hidden Defaults

Set your elements to start hidden and slightly offset:

<div class="opacity-0 transition-all duration-700" data-animate>
  <h2 class="text-3xl font-bold">Scroll-Direction Aware Element</h2>
</div>

Step 3: Create a Scroll Direction Tracker

We track scroll position changes to determine direction:

<script>
let lastScrollY = window.scrollY;
let scrollDirection = 'down';

window.addEventListener('scroll', () => {
  const currentScrollY = window.scrollY;
  scrollDirection = currentScrollY > lastScrollY ? 'down' : 'up';
  lastScrollY = currentScrollY;
});
</script>

Step 4: Set Up IntersectionObserver With Direction Logic

Combine IntersectionObserver and our scroll direction state:

<script>
const observer = new IntersectionObserver((entries) => {
  entries.forEach(entry => {
    if (entry.isIntersecting) {
      entry.target.classList.remove('opacity-0');
      if (scrollDirection === 'down') {
        entry.target.classList.add('animate-fadeSlideUp');
      } else {
        entry.target.classList.add('animate-fadeSlideDown');
      }
      observer.unobserve(entry.target);
    }
  });
}, {
  threshold: 0.2,
});

document.querySelectorAll('[data-animate]').forEach(el => observer.observe(el));
</script>

Now the animation direction dynamically matches the user's scroll intent!

Pros and Cons

✅ Pros

  • Zero external libraries required
  • Professional, dynamic feel with simple code
  • Extremely lightweight for high-performance web apps

⚠️ Cons

  • Only triggers once per element (could add re-entry logic if needed)
  • Needs a little extra logic for fast scrolls or edge cases

🚀 Alternatives

  • Framer Motion + React IntersectionObserver: for very fine-grained React control
  • GSAP ScrollTrigger: for physics-based animations and complex timelines

Summary

Scroll-direction aware animations create a rich, intuitive experience for your users — and with just Tailwind and a few lines of JavaScript, you can add these pro-level behaviors with zero bloat. Definitely worth weaving into your next modern frontend build!

If you found this useful, you can support me here: buymeacoffee.com/hexshift

Heroku

Built for developers, by developers.

Whether you're building a simple prototype or a business-critical product, Heroku's fully-managed platform gives you the simplest path to delivering apps quickly — using the tools and languages you already love!

Learn More

Top comments (1)

Collapse
 
maramanuel68 profile image
Mara Manuel

oukey...