Instructions
Webflow template user guide
GSAP Guide
Every GSAP code used on this template is here. How to edit them and find them is explain on this page. In every code block on this page, we added additional explanation to help you understand everything.

You can find the code in (Site settings) Footer Code.
Navbar width scroll animations
This script enhances the desktop navigation experience by dynamically adjusting the navbar's footprint based on scroll behavior. When users scroll down, the .block-navbar element automatically shrinks to 60% width to provide more screen real estate for content, expanding back to 100% upon scrolling up. Additionally, the .bg-navbar element features a smart opacity transition that smoothly fades the background in and out between 5% and 80% of the total page height, ensuring the menu remains legible without obstructing the hero design.
/* ================= NAVBAR ================= */
if (window.innerWidth >= 992) {
  let navbar = document.querySelector('.block-navbar');
  let bgNavbar = document.querySelector('.bg-navbar');
  let lastScroll = 0;

  if (navbar && bgNavbar) {
    window.addEventListener('scroll', () => {
      let currentScroll = window.pageYOffset;
      let docHeight = document.documentElement.scrollHeight - window.innerHeight;
      let scrollPercent = (currentScroll / docHeight) * 100;

      if (currentScroll > lastScroll) {
        gsap.to(navbar, {
          width: '60%',
          duration: 0.8,
        });
      } else {
        gsap.to(navbar, {
          width: '100%',
          duration: 0.8,
        });
      }

      lastScroll = currentScroll;

      let opacityValue = scrollPercent >= 5 && scrollPercent <= 80 ? 1 : scrollPercent / 5;
      gsap.to(bgNavbar, {
        opacity: opacityValue,
        duration: 0.3,
      });
    });
  }
}
Scramble text animation
This effect adds a high-tech, "decoding" feel to your headlines by shuffling random characters that gradually resolve into your actual text. Triggered automatically as the element enters the viewport, the .scramble class uses a 1.2-second duration to create a dynamic, eye-catching intro that is perfect for futuristic or developer-focused designs.
/* ================= SCRAMBLE TEXT ================= */
document.querySelectorAll('.scramble').forEach((el) => {
  let originalText = el.textContent;

  gsap.to(el, {
    scrollTrigger: {
      trigger: el,
      start: 'top 80%',
      toggleActions: 'play none play reverse',
    },
    duration: 1.2,
    scrambleText: {
      text: originalText,
    },
  });
});
Dropdown animation easing
Replace standard, abrupt Webflow transitions with GSAP-powered hover states for a more responsive UI. By applying the .dropdown class to a parent element and .wrapper-dropdown to the hidden menu, the script manages the display and opacity states through code rather than native interactions. This results in a silky-smooth fade-in and fade-out transition that is precisely timed, providing a premium feel to your navigation menus and sub-links that feels integrated and intentional.
/* ================= DROPDOWN ================= */
document.querySelectorAll('.dropdown').forEach((dropdown) => {
  let menu = dropdown.querySelector('.wrapper-dropdown');
  if (!menu) return;

  gsap.set(menu, {
    opacity: 0,
    display: 'none',
  });

  dropdown.addEventListener('mouseenter', () => {
    gsap.set(menu, {
      display: 'block',
    });
    gsap.to(menu, {
      opacity: 1,
      duration: 0.3,
    });
  });

  dropdown.addEventListener('mouseleave', () => {
    gsap.to(menu, {
      opacity: 0,
      duration: 0.2,
      onComplete: () =>
        gsap.set(menu, {
          display: 'none',
        }),
    });
  });
});
Heading text animation
Designed for main titles, the .text-entrance class utilizes a sophisticated character-splitting technique to animate your headings. Each letter is treated as an individual object, sliding upward from a 30px offset with a staggered fade-in, creating a synchronized, cinematic "wave" effect that feels premium and polished.
/* ================= TEXT ENTRANCE ================= */
document.querySelectorAll('.text-enterance').forEach((el) => {
  let split = new SplitType(el, {
    types: 'words, chars',
  });

  gsap.from(split.chars, {
    opacity: 0,
    y: 30,
    duration: 1,
    stagger: 0.03,
    scrollTrigger: {
      trigger: el,
      start: 'top 85%',
    },
  });
});
Descriptions Heading animation
For secondary headers or longer descriptions, the .desc-entrance class provides a smooth, elegant transition that combines movement with color logic. As you scroll, the text slides up into position while transitioning from a signature accent purple to a deep neutral black, ensuring your supporting content draws attention without overwhelming the main headline.
/* ================= DESCRIPTION ENTRANCE ================= */
document.querySelectorAll('.desc-entrance').forEach((el) => {
  gsap.fromTo(
    el,
    {
      opacity: 0,
      color: '#7C5CFF',
      y: 30,
    },
    {
      opacity: 1,
      color: '#111111',
      y: 0,
      duration: 1.5,
      ease: 'power2.out',
      scrollTrigger: {
        trigger: el,
        start: 'top 85%',
        toggleActions: 'play none play reverse',
      },
      clearProps: 'transform',
    },
  );
});
Tagline Heading animation
The .tag-line-block is a specialized entrance animation tailored for small labels or introductory taglines. It features a controlled fade-in and vertical slide with a "power2" easing curve, providing a clean and professional appearance that helps establish the hierarchy of your page before the main content arrives.
/* ================= TAGLINE ENTRANCE ================= */
document.querySelectorAll('.tag-line-block').forEach((el) => {
  gsap.fromTo(
    el,
    {
      opacity: 0,
      y: 30,
    },
    {
      opacity: 1,
      y: 0,
      duration: 1.2,
      ease: 'power2.inOut',
      scrollTrigger: {
        trigger: el,
        start: 'top 85%',
        toggleActions: 'play none play reverse',
      },
    },
  );
});
Draggable & infinite marquee integrations card
The integration marquee system is designed for seamless, continuous motion with full user interactivity. Elements within the .top and .bottom rows of the .main-content-integrations wrapper will loop infinitely in opposite directions at a controlled speed. Beyond the automatic movement, the script includes Draggable functionality, allowing users to manually swipe or drag the marquee left or right. The logic ensures that as items exit one side of the container, they are instantly repositioned at the end, maintaining a never-ending loop without any visual breaks.
<script>
//Start Draggable
gsap.registerPlugin(Draggable);

document.addEventListener("DOMContentLoaded", function () {

  function createMarquee(selector, speedValue = 0.8) {
    const track = document.querySelector(selector);
    if (!track) return;

    const container = track.parentElement;
    const trackStyle = window.getComputedStyle(track);
    const gap = parseFloat(trackStyle.columnGap) || 0;

    if (!track.dataset.looped) {
      track.innerHTML += track.innerHTML;
      track.dataset.looped = "true";
    }

    let x = 0;
    const setX = gsap.quickSetter(track, "x", "px");

    function doUpdate() {
      const containerRect = container.getBoundingClientRect();
      let currentX = gsap.getProperty(track, "x");

      let first = track.children[0];
      if (first) {
        let firstRect = first.getBoundingClientRect();
        if (firstRect.right < containerRect.left) {
          const shift = first.offsetWidth + gap;
          track.appendChild(first);
          currentX += shift;
          gsap.set(track, { x: currentX });
        }
      }

      let last = track.children[track.children.length - 1];
      if (last) {
        let lastRect = last.getBoundingClientRect();
        if (lastRect.left > containerRect.right) {
          const shift = last.offsetWidth + gap;
          track.insertBefore(last, track.firstChild);
          currentX -= shift;
          gsap.set(track, { x: currentX });
        }
      }
      
      return currentX;
    }

    const dragInstance = Draggable.create(track, {
      type: "x",
      onDrag: function() {
        x = this.x;
        x = doUpdate(); 
        this.update();  
      },
      onRelease: function() {
        x = gsap.getProperty(track, "x");
      }
    })[0];

    function render() {
      if (!dragInstance.isPressed && !dragInstance.isDragging) {
        x += speedValue; 
        setX(x);
        x = doUpdate();
      }
      requestAnimationFrame(render);
    }

    render();
  }


  createMarquee(".main-content-integrations.top", -0.8);
  createMarquee(".main-content-integrations.bottom", 0.8);
});

  //End Draggable
</script>