Instructions

A comprehensive guide to customizing and managing the advanced GSAP animations and interactive components within the template.

Developer & Customization Guide: GSAP Animations

This template leverages GSAP (GreenSock Animation Platform) to create premium, interactive user experiences. If your template consumers rename classes inside Webflow, they must match those updates inside the respective page code fields.

1. Home Page: Section Recap Scroll Animation

This section utilizes GSAP's ScrollTrigger to create a sticky parallax reveal effect. As users scroll down, the items smoothly slide up while their inner content counters the movement, creating a dynamic masking effect.

How to Use & Customize

  • Classes to Maintain: The script targets specific classes to function correctly. Ensure .recap_sticky, .recap_item, .recap_trigger, and .recap_inner remain unchanged in your Webflow structure. If you rename them in Webflow, you must update the corresponding variables in the code.
  • Animation Behavior: The animation is tied directly to the user's scroll position (scrub: 1). The distance the elements move is calculated based on the viewport height (vh).
  • Adding/Removing Items: The logic automatically calculates the sequence based on the number of items. Just make sure that for every .recap_item you add, there is a corresponding .recap_trigger element to activate it.

Code Configuration

< script >
  window.Webflow ||= [];
window.Webflow.push(() => {

  gsap.registerPlugin(ScrollTrigger);

  const ctx = gsap.context(() => {

    const items = gsap.utils.toArray(".recap_sticky .recap_item");
    const triggers = gsap.utils.toArray(".recap_trigger");

    if (items.length < 2) return;

    const vh = window.innerHeight;

    triggers.forEach((trigger, i) => {

      const item = items[items.length - 1 - i];
      const inner = item?.querySelector(".recap_inner");

      if (!item || !inner) return;

      gsap.timeline({
          scrollTrigger: {
            trigger,
            start: "top top",
            end: "bottom top",
            scrub: true
          }
        })
        .to(item, {
          y: -vh,
          ease: "none"
        }, 0)
        .to(inner, {
          y: vh,
          ease: "none"
        }, 0);

    });
  });
  return () => ctx.revert();

}); <
/script>

2. Home Page: Hero Section, Popup Reveal & Swiper Activation

A GSAP timeline sequence that dynamically reveals a popup modal on page load. Once the user dismisses the popup, it seamlessly triggers the appearance of the main hero swiper.

How to Use & Customize

  • Classes to Maintain: The script explicitly targets .section_popup, .popup_content, .primary-btn (used as the close button), and .hero_swiper. If you rename any of these in Webflow, update the corresponding variables at the top of the code.
  • Initial Delay: The popup opens automatically after a 1.3s delay to accommodate page loading. You can adjust this timing by changing delay: 1.3 inside the script.
  • Sequential Reveal Logic: When the close button is clicked, an onComplete callback runs at the end of the timeline. This function completely hides the popup and automatically fades the .hero_swiper into full visibility.

Code Configuration

<script>
window.Webflow ||= [];
window.Webflow.push(() => {

  // ── Popup Animation
  const popup = document.querySelector(".section_popup");
  const content = document.querySelector(".popup_content");
  const closeBtn = document.querySelector(".primary-btn");
  const heroSwiper = document.querySelector(".hero_swiper");

  if (popup && content && closeBtn) {
    // Initial state
    gsap.set(popup, { display: "none", opacity: 0 });
    gsap.set(content, { scale: 0.85, opacity: 0, y: 30 });
    gsap.set(heroSwiper, { opacity: 0, visibility: "hidden" }); 
    
    // 1.3s delay and popup open
    gsap.timeline({ delay: 1.3 })
      .to(popup, {
        display: "flex",
        opacity: 1,
        duration: 0.35,
        ease: "power2.out",
      })
      .to(content, {
        scale: 1,
        opacity: 1,
        y: 0,
        duration: 0.3,
        ease: "back.out(1.4)",
      }, "-=0.1");

    // Close button click
    closeBtn.addEventListener("click", function() {
      gsap.timeline()
        .to(content, {
          scale: 0.85,
          opacity: 0,
          y: 30,
          duration: 0.35,
          ease: "power2.in",
        })
        .to(popup, {
          opacity: 0,
          duration: 0.3,
          ease: "power2.in",
          onComplete: () => {
            gsap.set(popup, { display: "none" });
            // Activates hero_swiper after popup closes
            gsap.to(heroSwiper, {
              opacity: 1,
              visibility: "visible",
              duration: 0.4,
              ease: "power2.out",
            });
          },
        }, "-=0.1");
    });
  }

}); 
</script>

3. Home Page: Interactive Buttons, SVG Running Dashed Border

An advanced interactive hover effect applied to the primary button. The script dynamically injects an SVG shape to create a continuous, animated dashed border whenever the user hovers over the element.

How to Use & Customize

  • Target Element: The animation currently targets the .primary-btn class. If your button class changes, update the closeBtn variable selector in the code.
  • Dynamic Injection: You do not need to build the SVG lines manually inside Webflow. The script dynamically creates the <svg> and <rect> tags and appends them to the button on the live site.
  • Color Customization: On hover, the button's background changes to #000000, and resets to #1b1b1b when the mouse leaves. Update these HEX codes in the backgroundColor tweens to match your global brand colors.
  • Dash Appearance: The dashed visual is generated by stroke-dasharray: 6 4. You can tweak these specific numbers in the rect.style.cssText block to adjust the dash length and spacing.

Code Configuration

<script>
window.Webflow ||= [];
window.Webflow.push(() => {

  //── Running Dashed Border on primary-btn ──
  const closeBtn = document.querySelector(".primary-btn");
  
  if (closeBtn) { 
    // SVG inject 
    const svg = document.createElementNS("http://www.w3.org/2000/svg", "svg");
    const rect = document.createElementNS("http://www.w3.org/2000/svg", "rect");
    
    svg.style.cssText = `
      position: absolute;
      inset: 0;
      width: 100%;
      height: 100%;
      pointer-events: none;
      opacity: 0;
      overflow: visible;      
    `;
    rect.setAttribute("x", "1");
    rect.setAttribute("y", "1");
    rect.setAttribute("width", "calc(100% - 2px)");
    rect.setAttribute("height", "calc(100% - 2px)");
    rect.setAttribute("rx", "10");
    rect.setAttribute("ry", "10");
    
    rect.style.cssText = `
      fill: none;
      stroke: rgba(255, 255, 255, 0.20);
      stroke-width: 1;
      stroke-dasharray: 6 4;
      stroke-dashoffset: 0;      
    `;
    svg.appendChild(rect);
    gsap.set(closeBtn, {position: "relative", overflow: "hidden"});      
    closeBtn.appendChild(svg);
    
    let runAnim = null;
    
    closeBtn.addEventListener("mouseenter", () => {
      gsap.killTweensOf(closeBtn);
      gsap.to(closeBtn, {
        backgroundColor: "#000000",
        duration: 0.45,
        ease: "power2.out"
      });
      
      // SVG fade in
      gsap.to(svg, {
        opacity: 1,
        duration: 0.5,
        ease: "power2.out"
      });
      
      // Running dash 
      runAnim = gsap.to(rect, {
        strokeDashoffset: -20,
        duration: 1.5,
        ease: "none",
        repeat: -1,
      });
    });
    
    closeBtn.addEventListener("mouseleave", () => {
      gsap.killTweensOf(closeBtn);
      gsap.killTweensOf(rect);
      if (runAnim) runAnim.kill();
      gsap.set(rect, { strokeDashoffset: 0 });
      
      gsap.to(closeBtn, {
        backgroundColor: "#1b1b1b",
        duration: 0.45,
        ease: "power2.out"
      });
      gsap.to(svg, {
        opacity: 0,
        duration: 0.5,
        ease: "power2.out"
      });
    });
  }

}); 
</script>

4. Home Page: Hero Section, Background Video Switcher

A dynamic background cross-fade system that smoothly transitions between multiple background videos whenever a designated
trigger button is clicked.

How to Use & Customize

  • Classes to Maintain: Ensure all your background video elements share the class .hero_bg-video. The element meant to trigger the slide change must have the class .hero_swipe.
  • Automatic Looping: The script automatically counts all elements with the .hero_bg-video class and cycles through them infinitely based on the total number.
  • Transition Effect: The transition uses a 0.6s GSAP fade. As the user clicks, the current video smoothly fades out (opacity: 0) while the next video simultaneously fades in (opacity: 1). Adjust the duration values if you prefer a slower or faster cross-fade.

Code Configuration

<script>
window.Webflow ||= [];
window.Webflow.push(() => {

  // Video switcher
  const videos = document.querySelectorAll(".hero_bg-video");
  const btn = document.querySelector(".hero_swipe");
  
  if (btn && videos.length > 0) {
    let current = 0;
    
    gsap.set(videos, {
      opacity: 0,
      display: "block"
    });
    gsap.set(videos[0], { opacity: 1 });
    
    btn.addEventListener("click", function() {
      const prev = current;
      current = (current + 1) % videos.length;
      
      gsap.to(videos[prev], {
        opacity: 0,
        duration: 0.6,
        ease: "power2.inOut"
      });
      gsap.to(videos[current], {
        opacity: 1,
        duration: 0.6,
        ease: "power2.inOut"
      });
    });
  }

});
</script>

5. Footer Section of all pages: Real-Time Digital Watch

A real-time digital clock located in the footer. It uses the gsap.ticker for smooth rendering and is synchronized to a specific timezone.

How to Use & Customize

  • Target Element ID: The clock script specifically looks for an element with the ID realtime-clock. Make sure your text block in Webflow has this exact ID applied in the element settings.
  • Target Element ID: The clock script specifically looks for an element with the ID realtime-clock. Make sure your text block in Webflow has this exact ID applied in the element settings.
  • Timezone Customization: By default, the clock is set to Singapore time ('Asia/Singapore'). To change this to your local timezone (e.g., New York or London), replace 'Asia/Singapore' in the toLocaleString function with your preferred IANA time zone string (e.g., 'America/New_York').
  • Time Format: The current output format is HH : MM : SS : 00. If you wish to remove the trailing : 00 or change the separators, you can modify the template literal in the clockEl.textContent line.

Code Configuration

(Note: I wrapped your snippet in a DOMContentLoaded event listener to ensure it loads properly without errors.)
< script >
  window.Webflow ||= [];
window.Webflow.push(() => {

    const clock = document.querySelector("#realtime-clock");

    if (clock) {

      const pad = n => String(n).padStart(2, "0");

      gsap.ticker.add(() => {

        const now = new Date();

        const sg = new Date(
          now.toLocaleString("en-US", {
            timeZone: "Asia/Singapore"
          })
        );

        clock.textContent =
          `${pad(sg.getHours())} : ${pad(sg.getMinutes())} : ${pad(sg.getSeconds())} : 00`;

      });

    }

  });

  return () => ctx.revert();

}); <
/script>