DEV Community

Cover image for Building Scroll-Triggered Animations in Tailwind CSS Without External Libraries
HexShift
HexShift

Posted on

1

Building Scroll-Triggered Animations in Tailwind CSS Without External Libraries

Scroll-based animations usually mean reaching for heavy libraries like GSAP or ScrollMagic. But with Tailwind CSS and the native IntersectionObserver API, you can create highly performant, elegant scroll-triggered animations — zero extra libraries required. Let's build a full setup.

Why Native Scroll Animations?

Benefits of ditching the big libraries:

  • Smaller bundle size, faster load times
  • Full control over when and how elements animate
  • Progressive enhancement for older browsers

Step 1: Build a Tailwind Animation Class

First, extend Tailwind with a custom fade/slide animation:

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

Step 2: Setup HTML Markup

Mark elements you want to animate with a hidden state:

<div class="opacity-0 translate-y-5 transition-all duration-700" data-animate>
  <h2 class="text-3xl font-bold">Reveal on Scroll</h2>
  <p>This section will fade and slide into view.</p>
</div>

Initially, the item is invisible and shifted slightly down.

Step 3: Use IntersectionObserver to Trigger Animation

Here's the vanilla JS magic:

<script>
const observer = new IntersectionObserver((entries) => {
  entries.forEach(entry => {
    if (entry.isIntersecting) {
      entry.target.classList.remove('opacity-0', 'translate-y-5');
      entry.target.classList.add('animate-fadeSlide');
      observer.unobserve(entry.target);
    }
  });
}, {
  threshold: 0.2,
});

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

Each element with data-animate will animate the first time it comes into view. Then we unobserve to avoid retriggering.

Pros and Cons

✅ Pros

  • Zero dependencies — full native performance
  • Highly customizable with Tailwind’s animation system
  • Accessibility-friendly (no reliance on JavaScript for critical content)

⚠️ Cons

  • Requires small JS snippet per page (can't be pure CSS-only)
  • Some older browsers (like IE11) don't support IntersectionObserver (polyfills needed if critical)

🚀 Alternatives

  • Framer Motion + React: For app-level React animation control
  • Locomotive Scroll: If you want smooth scroll + parallax bundled together

Summary

Scroll-triggered animations don't need to bloat your app. With a pinch of Tailwind CSS and IntersectionObserver, you can deliver buttery-smooth, accessible motion experiences with minimal code — keeping your UI dynamic and your performance sharp.

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

Heroku

Amplify your impact where it matters most — building exceptional apps.

Leave the infrastructure headaches to us, while you focus on pushing boundaries, realizing your vision, and making a lasting impression on your users.

Get Started

Top comments (0)

PulumiUP 2025 image

PulumiUP 2025: Cloud Innovation Starts Here

Get inspired by experts at PulumiUP. Discover the latest in platform engineering, IaC, and DevOps. Keynote, demos, panel, and Q&A with Pulumi engineers.

Register Now

👋 Kindness is contagious

Engage with a wealth of insights in this thoughtful article, valued within the supportive DEV Community. Coders of every background are welcome to join in and add to our collective wisdom.

A sincere "thank you" often brightens someone’s day. Share your gratitude in the comments below!

On DEV, the act of sharing knowledge eases our journey and fortifies our community ties. Found value in this? A quick thank you to the author can make a significant impact.

Okay