ALVION text logo with copyright symbol.Dark stylized lowercase letter 'a' logo with curved elements forming a symmetrical design.

Instructions

Find easy to follow instructions

GSAP Guide
Animation Code
All GSAP animations used in this template are collected here. On this page, you’ll find guidance on how to locate and edit them. Each code block comes with extra notes to make it easier to understand.
Custom Code
Lenis Scroll
Smooth scrolling effect for the template using Lenis with GSAP integration for precise scroll updates and animations.
<link rel="stylesheet" href="https://unpkg.com/lenis@1.3.15/dist/lenis.css" />
<script src="https://unpkg.com/lenis@1.3.15/dist/lenis.min.js"></script>

<script>
  const lenis = new Lenis({ duration: 1.4 });

  lenis.on('scroll', ScrollTrigger.update);
  gsap.ticker.add((time) => {
    lenis.raf(time * 1000);
  });
  gsap.ticker.lagSmoothing(0);
</script>
Hero Scroll Blur & Logo Scale
Subtle scroll-triggered animations using GSAP. As users scroll into the .track-about section, the hero background gains a smooth blur while the logo header scales down responsively, creating a polished and dynamic visual effect.
<script>
  window.addEventListener('load', () => {
    if (!document.querySelector('.track-about')) return;
    gsap.registerPlugin(ScrollTrigger);

    // Backdrop filter bg-hero
    gsap.fromTo(
      '.filter-bg-hero',
      { backdropFilter: 'blur(0px)', webkitBackdropFilter: 'blur(0px)' },
      {
        backdropFilter: 'blur(12px)',
        webkitBackdropFilter: 'blur(12px)',
        scrollTrigger: {
          trigger: '.track-about',
          start: 'top+=20% center',
          end: 'bottom top',
          toggleActions: 'play none play reverse',
        },
      },
    );

    // Logo header scale
    gsap.fromTo(
      '.logo-header',
      { width: '32vw' },
      {
        width: () => (window.innerWidth <= 768 ? '17vw' : '12vw'),
        duration: 0.75,
        ease: 'power3.inOut',
        scrollTrigger: {
          trigger: '.track-about',
          start: 'top+=20% center',
          end: 'bottom top',
          toggleActions: 'play none play reverse',
        },
      },
    );
  });
</script>
GSAP Preload Scroll Lock
Smooth preload experience with GSAP-based scroll locking system. The page is temporarily fixed during the loading sequence to ensure a controlled entry experience, then restored with proper scroll reset and ScrollTrigger refresh for seamless animations.
<script>
  window.addEventListener('load', () => {
    if (!document.querySelector('.preload')) return;
    const preloadWrapper = document.querySelector('.preload');

    document.documentElement.scrollTop = 0;
    document.body.scrollTop = 0;

    gsap.delayedCall(0.05, () => {
      document.documentElement.scrollTop = 0;
      document.body.scrollTop = 0;

      gsap.set('body', {
        position: 'fixed',
        top: '0px',
        width: '100%',
        overflow: 'hidden',
      });
    });

    gsap.delayedCall(3.8, () => {
      if (preloadWrapper) {
        gsap.to(preloadWrapper, {
          opacity: 0,
          duration: 0.5,
          onComplete: () => {
            preloadWrapper.style.display = 'none';
          },
        });
      }

      gsap.set('body', {
        position: 'static',
        overflow: 'auto',
        top: '0',
      });

      window.scrollTo(0, 0);
      document.documentElement.scrollTop = 0;

      if (typeof ScrollTrigger !== 'undefined') {
        ScrollTrigger.refresh();
      }
    });
  });
</script>
Smooth Follow Cursor
Custom cursor implementation using GSAP quickTo, creating a smooth, inertia-based movement that follows the user's mouse across interactive areas.
<script>
  document.addEventListener('DOMContentLoaded', function () {
    const triggerWrap = document.querySelector('.wrapper-cursor');
    const cursorFollow = document.querySelector('.cursor-core');
    const triggers = document.querySelectorAll('.wrapper-cursor');

    if (!triggerWrap || !cursorFollow) return;
    const xTo = gsap.quickTo(cursorFollow, 'x', { duration: 0.4, ease: 'power3.out' });
    const yTo = gsap.quickTo(cursorFollow, 'y', { duration: 0.4, ease: 'power3.out' });

    window.addEventListener('mousemove', (e) => {
      xTo(e.clientX);
      yTo(e.clientY);
    });
  });
</script>
Team Info Navigator
Interactive team showcase using GSAP animations. Click the left or right triggers to smoothly transition between team member blocks, expanding the selected block to highlight content while hiding the others.
<script>
  document.addEventListener('DOMContentLoaded', function () {
    const rightTrigger = document.querySelector('.right-click-team');
    const leftTrigger = document.querySelector('.left-click-team');
    const blocks = document.querySelectorAll('.block-image-team');
    const infos = document.querySelectorAll('.wrapper-info-team');

    const HEIGHT = '80vh';
    let activeIndex = 0;
    let isAnimating = false;

    gsap.set(blocks[0], { height: HEIGHT });
    gsap.set(blocks[1], { height: 0 });
    gsap.set(blocks[2], { height: 0 });
    gsap.set(blocks[3], { height: 0 });

    gsap.set(infos[0], { opacity: 1 });
    gsap.set(infos[1], { opacity: 0 });
    gsap.set(infos[2], { opacity: 0 });
    gsap.set(infos[3], { opacity: 0 });

    function goToNext() {
      if (isAnimating) return;
      isAnimating = true;

      if (activeIndex === 0) {
        gsap.to(blocks[0], { height: 0, duration: 0.5 });
        gsap.to(blocks[1], { height: HEIGHT, duration: 0.5 });
        gsap.to(infos[0], { opacity: 0, duration: 0.4 });
        gsap.to(infos[1], { opacity: 1, duration: 0.4 });
        activeIndex = 1;
      } else if (activeIndex === 1) {
        gsap.to(blocks[1], { height: 0, duration: 0.5 });
        gsap.to(blocks[2], { height: HEIGHT, duration: 0.5 });
        gsap.to(infos[1], { opacity: 0, duration: 0.4 });
        gsap.to(infos[2], { opacity: 1, duration: 0.4 });
        activeIndex = 2;
      } else if (activeIndex === 2) {
        gsap.to(blocks[2], { height: 0, duration: 0.5 });
        gsap.to(blocks[3], { height: HEIGHT, duration: 0.5 });
        gsap.to(infos[2], { opacity: 0, duration: 0.4 });
        gsap.to(infos[3], { opacity: 1, duration: 0.4 });
        activeIndex = 3;
      }

      setTimeout(() => {
        isAnimating = false;
      }, 500);
    }

    function goToPrev() {
      if (isAnimating) return;
      isAnimating = true;

      if (activeIndex === 3) {
        gsap.to(blocks[3], { height: 0, duration: 0.5 });
        gsap.to(blocks[2], { height: HEIGHT, duration: 0.5 });
        gsap.to(infos[3], { opacity: 0, duration: 0.4 });
        gsap.to(infos[2], { opacity: 1, duration: 0.4 });
        activeIndex = 2;
      } else if (activeIndex === 2) {
        gsap.to(blocks[2], { height: 0, duration: 0.5 });
        gsap.to(blocks[1], { height: HEIGHT, duration: 0.5 });
        gsap.to(infos[2], { opacity: 0, duration: 0.4 });
        gsap.to(infos[1], { opacity: 1, duration: 0.4 });
        activeIndex = 1;
      } else if (activeIndex === 1) {
        gsap.to(blocks[1], { height: 0, duration: 0.5 });
        gsap.to(blocks[0], { height: HEIGHT, duration: 0.5 });
        gsap.to(infos[1], { opacity: 0, duration: 0.4 });
        gsap.to(infos[0], { opacity: 1, duration: 0.4 });
        activeIndex = 0;
      }

      setTimeout(() => {
        isAnimating = false;
      }, 500);
    }

    rightTrigger.addEventListener('click', goToNext);
    leftTrigger.addEventListener('click', goToPrev);
  });
</script>
Interactive Testimonial Cards
A horizontal testimonial showcase with dynamic width animations. The first card starts at a larger default width, while others are smaller.
<script>
  window.addEventListener('load', () => {
    // DESKTOP & TABLET ONLY
    if (!window.matchMedia('(min-width: 768px)').matches) return;

    const card1 = document.querySelector('.card-testimonials.v1');
    const card2 = document.querySelector('.card-testimonials.v2');
    const card3 = document.querySelector('.card-testimonials.v3');
    const card4 = document.querySelector('.card-testimonials.v4');

    const HOVER_WIDTH = '43vw';
    const OTHER_WIDTH = '19vw';
    const DEFAULT_WIDTH = '43vw';

    // set initial widths
    gsap.set(card1, {
      width: DEFAULT_WIDTH
    });
    gsap.set(card2, {
      width: OTHER_WIDTH
    });
    gsap.set(card3, {
      width: OTHER_WIDTH
    });
    gsap.set(card4, {
      width: OTHER_WIDTH
    });

    function hoverCard(active, others) {
      gsap.to(active, {
        width: HOVER_WIDTH,
        duration: 0.6,
        ease: 'expo.out'
      });
      others.forEach((c) => gsap.to(c, {
        width: OTHER_WIDTH,
        duration: 0.6,
        ease: 'expo.out'
      }));
    }

    function resetCards() {
      gsap.to(card1, {
        width: DEFAULT_WIDTH,
        duration: 0.6,
        ease: 'power2.out'
      });
      gsap.to(card2, {
        width: OTHER_WIDTH,
        duration: 0.6,
        ease: 'power2.out'
      });
      gsap.to(card3, {
        width: OTHER_WIDTH,
        duration: 0.6,
        ease: 'power2.out'
      });
      gsap.to(card4, {
        width: OTHER_WIDTH,
        duration: 0.6,
        ease: 'power2.out'
      });
    }

    // Card 1 hover
    card1.addEventListener('mouseenter', () => hoverCard(card1, [card2, card3, card4]));
    card1.addEventListener('mouseleave', resetCards);

    // Card 2 hover
    card2.addEventListener('mouseenter', () => hoverCard(card2, [card1, card3, card4]));
    card2.addEventListener('mouseleave', resetCards);

    // Card 3 hover
    card3.addEventListener('mouseenter', () => hoverCard(card3, [card1, card2, card4]));
    card3.addEventListener('mouseleave', resetCards);

    // Card 4 hover
    card4.addEventListener('mouseenter', () => hoverCard(card4, [card1, card2, card3]));
    card4.addEventListener('mouseleave', resetCards);
  }); 
</script>