instructions

How It Works

Learn how to customize this template and create a website that fits your brand.
GSAP Setup & Main Functions
All scripts go in Webflow Page Settings → Before </body> tag (or in Site Settings → Custom Code → Footer Code if global).

Webflow Site Settings → GSAP must have Draggable enabled. Inertia optional (script has fallback).
1. Main Cursor
What it does: Custom cursor that smoothly follows the mouse. Exposes window._cursorPause() and window._cursorResume() so other scripts can hide it on demand.
Required markup:
.cursor-core — outer cursor element (animated container)
.cursor-pointer — inner visual (the actual circle, can be hidden via pause/resume)
<script>
window.Webflow = window.Webflow || [];
window.Webflow.push(function () {

  // ==================================================
  // MAIN CURSOR
  // ==================================================

  if (window._cursorCoreInit) return;
  window._cursorCoreInit = true;

  var cursor = document.querySelector(".cursor-core");
  var pointer = document.querySelector(".cursor-pointer");

  if (!cursor) return;

  var mouseX = -100;
  var mouseY = -100;

  var x = -100;
  var y = -100;

  var paused = false;

  // initial
  gsap.set(cursor, {
    xPercent: -50,
    yPercent: -50,
    x: x,
    y: y
  });

  // smooth follow
  gsap.ticker.add(function () {

    x += (mouseX - x) * 0.15;
    y += (mouseY - y) * 0.15;

    gsap.set(cursor, {
      x: x,
      y: y
    });

  });

  // track mouse
  window.addEventListener("mousemove", function (e) {

    mouseX = e.clientX;
    mouseY = e.clientY;

  });

  // =========================================
  // HIDE POINTER ONLY
  // =========================================

  window._cursorPause = function () {

    paused = true;

    if (!pointer) return;

    gsap.to(pointer, {
      opacity: 0,
      duration: 0.2,
      overwrite: true
    });

  };

  // =========================================
  // SHOW POINTER AGAIN
  // =========================================

  window._cursorResume = function () {

    paused = false;

    if (!pointer) return;

    gsap.to(pointer, {
      opacity: 1,
      duration: 0.2,
      overwrite: true
    });

  };

});
</script>
2. Counter Number Animation
What it does: Animates numbers from 0 to their final value when they enter the viewport. The animation automatically detects the first child element inside .counter-number, making it compatible with any typography class (such as .text-l, .text-xl, or .text-xxl). Numeric suffixes like %, +, or yrs are preserved throughout the animation.
Required markup:
.counter-number —  the wrapper element that triggers the counter animation
<script>
window.Webflow = window.Webflow || [];
window.Webflow.push(function () {

  gsap.registerPlugin(ScrollTrigger);

  document.querySelectorAll(".counter-number").forEach((counter) => {

    // Get the first child element (.text-xl, .text-l, .text-xxl, etc.)
    const number = counter.firstElementChild;
    if (!number) return;

    const text = number.textContent.trim();
    const finalValue = parseInt(text.replace(/[^\d]/g, ""), 10);
    if (isNaN(finalValue)) return;

    // Preserve any non-numeric suffix (%, +, yrs, etc.)
    const suffix = text.replace(/[\d\s]/g, "");

    // Create an object to animate
    const obj = { value: 0 };

    gsap.to(obj, {
      value: finalValue,
      duration: 2,
      ease: "expo.out",
      snap: { value: 1 },

      scrollTrigger: {
        trigger: counter,
        start: "top 85%",
        once: true
      },

      onUpdate() {
        number.textContent = Math.round(obj.value) + suffix;
      }
    });

  });

});
</script>