DEV Community

Cover image for Building Scroll-Linked Parallax Effects in Pure Tailwind and Vanilla JavaScript
HexShift
HexShift

Posted on

Building Scroll-Linked Parallax Effects in Pure Tailwind and Vanilla JavaScript

Parallax scrolling — where elements move at different speeds relative to scrolling — can make a website feel deeply dynamic. Most people use heavy libraries like GSAP for this. But today, we’ll build a clean, efficient scroll-linked parallax effect with just Tailwind CSS and vanilla JavaScript. No external dependencies.

Why Go Pure?

Benefits include:

  • Massively reduced page weight
  • Customizable to any design system
  • Better performance for mobile users

Step 1: Create Your Layers

Each parallax layer gets a basic Tailwind setup:

<section class="relative h-[200vh] overflow-hidden">
  <div class="absolute top-0 left-0 w-full h-full" data-parallax data-speed="0.3">
    <img src="/background-layer.jpg" class="w-full h-full object-cover" />
  </div>
  <div class="absolute top-0 left-0 w-full h-full" data-parallax data-speed="0.6">
    <img src="/midground-layer.png" class="w-full h-full object-cover opacity-80" />
  </div>
  <div class="relative z-10 flex items-center justify-center h-screen">
    <h1 class="text-6xl font-bold text-white">Scroll Down</h1>
  </div>
</section>

Step 2: Basic JavaScript for Parallax

This snippet calculates parallax based on scroll position:

<script>
const parallaxEls = document.querySelectorAll('[data-parallax]');

window.addEventListener('scroll', () => {
  const scrollTop = window.pageYOffset;
  
  parallaxEls.forEach(el => {
    const speed = parseFloat(el.getAttribute('data-speed'));
    el.style.transform = `translateY(${scrollTop * speed}px)`;
  });
});
</script>

Step 3: Tuning Speeds Per Layer

You can assign different speeds to different layers with data-speed. Smaller values (e.g., 0.1) move slower (background feel), larger values (e.g., 0.6) move faster (foreground feel).

Bonus: Smoother Motion with requestAnimationFrame

For buttery smoothness, swap in a rAF loop instead of basic scroll event:

<script>
let latestScroll = 0;
let ticking = false;

window.addEventListener('scroll', () => {
  latestScroll = window.pageYOffset;
  if (!ticking) {
    window.requestAnimationFrame(() => {
      parallaxEls.forEach(el => {
        const speed = parseFloat(el.getAttribute('data-speed'));
        el.style.transform = `translateY(${latestScroll * speed}px)`;
      });
      ticking = false;
    });
    ticking = true;
  }
});
</script>

Pros and Cons

✅ Pros

  • No libraries — maximum control and minimal load time
  • Fully responsive and easy to tweak per element
  • Compatible with Tailwind’s utility-first workflow

⚠️ Cons

  • Manual math if you want more complex motion curves
  • Very fast scrolls might cause tiny visual tearing without fine-tuning

🚀 Alternatives

  • Locomotive Scroll: Adds inertia and smooth-scroll parallax but is a heavy dependency
  • GSAP ScrollTrigger: Amazing for animation timelines, but overkill if you only want basic parallax

Summary

Creating scroll-linked parallax in Tailwind and vanilla JavaScript is refreshingly simple and surprisingly powerful. With just a few utility classes and event listeners, you can add rich motion to your site without slowing it down — a massive win for modern frontend builds!

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

Top comments (1)

Collapse
 
nevodavid profile image
Nevo David

been thinking about dropping the big libraries myself so this feels like the move