How to Add Parallax and Scroll Animations to GenerateBlocks with GSAP

Coming from feature-rich themes like Enfold, Avada, or Divi to GeneratePress can feel like a trade-off. Yes, you get blazing fast performance and clean code, but suddenly those built-in parallax effects, animated sections, and fancy scrolling features are gone. And that’s by design – GeneratePress stays lean by not including every possible feature.

But here’s what I’ve learned after migrating dozens of client sites to GeneratePress: you don’t have to sacrifice those professional effects. You just need to be more intentional about adding them. When a client asks for parallax scrolling, animations, or interactive elements, instead of reaching for a heavy plugin that adds jQuery dependencies and 50 features you’ll never use, you can add exactly what you need with GSAP.

This tutorial shows you how to add smooth parallax effects and scroll animations using GenerateBlocks and GSAP ScrollTrigger – keeping the lightweight philosophy that made you choose GeneratePress in the first place. GSAP is the industry standard for web animations, used by Apple, Google, and Nike. While it’s larger than single-purpose libraries (70kb), it replaces multiple animation plugins with one professional-grade solution. Want to dive deeper? Check out [GSAP’s extensive learning resources](https://gsap.com/resources/).

In under 10 minutes, you’ll have parallax, fade animations, diagonal movement, and more – all the effects those page builder themes offer, but with better performance and more control.

Step 1 — Enqueue GSAP ScrollTrigger

First, let’s add GSAP to your site with conditional loading – it only loads on pages that actually use the animations. Add this code to your child theme’s functions.php or wherever you manage your scripts.

function enqueue_gsap_scrolltrigger() {
    global $post;
    
    // Only check on singular pages
    if (is_singular() && isset($post->post_content)) {
        // Check if page content contains gsap classes
        if (strpos($post->post_content, 'gsap-') !== false) {
            // Enqueue GSAP core
            wp_enqueue_script('gsap', 'https://cdn.jsdelivr.net/npm/[email protected]/dist/gsap.min.js', array(), '3.12.5', true);
            
            // Enqueue ScrollTrigger plugin
            wp_enqueue_script('gsap-scrolltrigger', 'https://cdn.jsdelivr.net/npm/[email protected]/dist/ScrollTrigger.min.js', array('gsap'), '3.12.5', true);
            
            // Enqueue CSS
            wp_enqueue_style('gsap-styles', get_stylesheet_directory_uri() . '/assets/css/gsap-scrolltrigger.css', array(), '1.0.0');
            
            // Initialize GSAP animations (see Step 3 for full code)
            wp_add_inline_script('gsap-scrolltrigger', $init_script);
        }
    }
}
add_action('wp_enqueue_scripts', 'enqueue_gsap_scrolltrigger');

This smart loading approach means GSAP only loads when you’ve actually added a gsap- class to a container – keeping pages without animations completely free of unnecessary scripts.

Step 2 — Add the CSS Utility Classes

Save this minified CSS as /assets/css/gsap-scrolltrigger.css in your child theme:

.gsap-parallax{will-change:transform}.gsap-fade-up,.gsap-fade-left,.gsap-fade-right,.gsap-scale{opacity:0}.gsap-horizontal{will-change:transform}.gsap-pin{position:relative}.gsap-hero{min-height:100vh;position:relative;overflow:hidden;display:flex;align-items:center;justify-content:center}.gsap-hero-bg{position:absolute;top:0;left:0;width:100%;height:120%;object-fit:cover;z-index:-1}.gsap-section{position:relative;overflow:hidden;padding:100px 0}.gsap-float{position:absolute;pointer-events:none}.gsap-stagger>*{opacity:0}.gsap-text-reveal{overflow:hidden}.gsap-text-reveal>*{transform:translateY(100%)}.gsap-rotate{will-change:transform}.gsap-morph{will-change:transform}.gsap-timeline{position:relative}.gsap-scrub{will-change:transform}.gsap-diagonal{will-change:transform}.gsap-layer-back{z-index:-2}.gsap-layer-mid{z-index:-1}.gsap-layer-front{z-index:1}.gsap-layer-top{z-index:10}@media(max-width:768px){.gsap-mobile-disable{transform:none!important}.gsap-mobile-hide{display:none}}

Step 3 — Initialize GSAP Animations

Add this initialization script to your enqueue function (this is the $init_script variable from Step 1):

$init_script = '
    gsap.registerPlugin(ScrollTrigger);
    
    // Vertical parallax
    gsap.utils.toArray(".gsap-parallax").forEach((elem) => {
        const speed = elem.dataset.speed || -5;
        gsap.to(elem, {
            yPercent: speed * 10,
            ease: "none",
            scrollTrigger: {
                trigger: elem,
                start: "top bottom",
                end: "bottom top",
                scrub: true
            }
        });
    });
    
    // Horizontal parallax
    gsap.utils.toArray(".gsap-horizontal").forEach((elem) => {
        const speed = elem.dataset.hspeed || 50;
        gsap.to(elem, {
            xPercent: speed,
            ease: "none",
            scrollTrigger: {
                trigger: elem,
                start: "top bottom",
                end: "bottom top",
                scrub: true
            }
        });
    });
    
    // Diagonal movement (the holy grail!)
    gsap.utils.toArray(".gsap-diagonal").forEach((elem) => {
        const vSpeed = elem.dataset.speed || -5;
        const hSpeed = elem.dataset.hspeed || 5;
        
        const tl = gsap.timeline({
            scrollTrigger: {
                trigger: elem,
                start: "top bottom",
                end: "bottom top",
                scrub: 1
            }
        });
        
        tl.to(elem, {
            yPercent: vSpeed * 10,
            xPercent: hSpeed * 10,
            ease: "none"
        });
    });
    
    // Fade animations (all directions)
    ["up", "down", "left", "right"].forEach(direction => {
        gsap.utils.toArray(`.gsap-fade-${direction}`).forEach((elem) => {
            const config = {
                opacity: 0,
                duration: elem.dataset.duration || 1,
                delay: elem.dataset.delay || 0,
                scrollTrigger: {
                    trigger: elem,
                    start: elem.dataset.start || "top 85%",
                    toggleActions: "play none none reverse"
                }
            };
            
            switch(direction) {
                case "up": config.y = elem.dataset.distance || 60; break;
                case "down": config.y = -(elem.dataset.distance || 60); break;
                case "left": config.x = -(elem.dataset.distance || 60); break;
                case "right": config.x = elem.dataset.distance || 60; break;
            }
            
            gsap.from(elem, config);
        });
    });
    
    // Scale animation
    gsap.utils.toArray(".gsap-scale").forEach((elem) => {
        gsap.from(elem, {
            scale: elem.dataset.scale || 0.8,
            opacity: 0,
            duration: elem.dataset.duration || 1,
            scrollTrigger: {
                trigger: elem,
                start: elem.dataset.start || "top 80%"
            }
        });
    });
    
    // Stagger animations for child elements
    gsap.utils.toArray(".gsap-stagger").forEach((elem) => {
        const children = elem.children;
        gsap.from(children, {
            y: elem.dataset.distance || 30,
            opacity: 0,
            duration: elem.dataset.duration || 0.6,
            stagger: elem.dataset.stagger || 0.1,
            delay: elem.dataset.delay || 0,
            scrollTrigger: {
                trigger: elem,
                start: elem.dataset.start || "top 80%"
            }
        });
    });
    
    // Pin sections
    gsap.utils.toArray(".gsap-pin").forEach((elem) => {
        ScrollTrigger.create({
            trigger: elem,
            start: elem.dataset.pinstart || "top top",
            end: elem.dataset.pinend || "bottom top",
            pin: true,
            pinSpacing: elem.dataset.pinspacing !== "false"
        });
    });
    
    // Counter animation
    gsap.utils.toArray(".gsap-counter").forEach((elem) => {
        const target = elem.dataset.target || 100;
        const duration = elem.dataset.duration || 2;
        const obj = { value: 0 };
        
        gsap.to(obj, {
            value: target,
            duration: duration,
            ease: "power1.inOut",
            scrollTrigger: {
                trigger: elem,
                start: "top 80%",
                once: true
            },
            onUpdate: () => {
                elem.textContent = Math.round(obj.value);
            }
        });
    });
';

Step 4 — Add Animations to Any Container

Adding animations is simple. On any GenerateBlocks container:

  1. Add a gsap- class to the Additional CSS Classes field
  2. Add data attributes for customization

That’s it. Your container now has professional animations.

GenerateBlocks Settings Examples:

For Parallax:

  • Additional CSS Classes: gsap-parallax
  • Custom Attributes:
    • Name: data-speed
    • Value: -7 (negative = slower, positive = faster)

For Diagonal Movement:

  • Additional CSS Classes: gsap-diagonal
  • Custom Attributes:
    • data-speed|-5 (vertical speed)
    • data-hspeed|10 (horizontal speed)

For Fade Animation:

  • Additional CSS Classes: gsap-fade-up
  • Custom Attributes (optional):
    • data-distance|80 (distance to travel)
    • data-duration|1.5 (animation duration)
    • data-delay|0.3 (delay before start)

Step 5 — Common Animation Patterns

Hero Section with Parallax Background

In GenerateBlocks:

  1. Create a Container with your hero content
  2. Add a background image via GB settings
  3. Add class: gsap-parallax gsap-hero
  4. Add attribute: data-speed|-7

Staggered Cards Animation

Perfect for feature boxes or team grids:

  1. Create a GB Grid container
  2. Add class: gsap-stagger
  3. Add attributes: data-stagger|0.2 data-distance|50
  4. All child containers will animate in sequence

Diagonal Floating Elements

For decorative elements that move diagonally:

<!-- In GB Custom HTML Attributes -->
class="gsap-diagonal"
data-speed="-10"
data-hspeed="20"

Fade In Content Sections

<!-- Fade from bottom -->
class="gsap-fade-up"
data-distance="100"
data-duration="1.2"

<!-- Fade from left -->
class="gsap-fade-left"
data-distance="80"
data-delay="0.3"

Pinned Section (Sticky Scrolling)

class="gsap-pin"
data-pinstart="top top"
data-pinend="+=500"

Animated Counters

<!-- For a number counter -->
class="gsap-counter"
data-target="1234"
data-duration="2.5"

Step 6 — Performance Optimization

Mobile Control

GSAP animations can be heavy on mobile. Add gsap-mobile-disable to any element to disable its animation on mobile devices (under 768px).

Reduce Animations

Don’t go overboard. 5-10 animated elements per page is plenty. More than that risks janky scrolling.

Use GPU Acceleration

The CSS file already includes will-change: transform for smooth animations, but avoid animating properties like width or height – stick to transform and opacity.

Available Animation Classes Reference

Parallax Effects

  • gsap-parallax – Vertical parallax (data-speed)
  • gsap-horizontal – Horizontal parallax (data-hspeed)
  • gsap-diagonal – Diagonal movement (data-speed, data-hspeed)

Fade Animations

  • gsap-fade – Simple fade
  • gsap-fade-up – Fade in from bottom
  • gsap-fade-down – Fade in from top
  • gsap-fade-left – Fade in from left
  • gsap-fade-right – Fade in from right

Transform Effects

  • gsap-scale – Scale in effect
  • gsap-rotate – Rotation on scroll (data-rotate)
  • gsap-flip – 3D flip animation

Advanced Effects

  • gsap-stagger – Animate children in sequence
  • gsap-pin – Pin element while scrolling
  • gsap-counter – Animated number counter
  • gsap-text-reveal – Reveal text line by line

Data Attributes

All animations support these optional data attributes:

  • data-speed – Vertical movement speed (-10 to 10)
  • data-hspeed – Horizontal movement speed
  • data-distance – Distance for fade animations
  • data-duration – Animation duration in seconds
  • data-delay – Delay before animation starts
  • data-start – ScrollTrigger start position
  • data-scale – Scale factor
  • data-stagger – Stagger timing for children

Troubleshooting

Animations not working? Check if the class starts with gsap- and the page was refreshed after adding it.

Diagonal movement not working? Make sure you’re using both data-speed and data-hspeed attributes with the gsap-diagonal class.

Performance issues? Add gsap-mobile-disable class or reduce the number of animated elements.

Elements jumping on load? The CSS file includes opacity settings to prevent this. Make sure it’s properly loaded.

Bonus: Quick Implementation in Query Loops

This setup works perfectly with GenerateBlocks Query Loops. Just add the animation classes to your Loop Item Block: