GSAP Skill

SkillAI & models

GreenSock Animation Platform — high-performance JS animation engine with plugin ecosystem.

Available today. Use it from your connected AI after setup.

Connect ahel once, and every AI you use reads what you have installed.

Then ask your AI: use the GSAP Skill skill

About this capability

Specialized agents, MCP servers, skills, and a visual dashboard for Claude Code. MIT.

What this skill tells your AI

The instructions your AI receives, as published by claude-dev-suite/claude-dev-suite in skills/animation/gsap/SKILL.md and read by ahel’s review.

GreenSock Animation Platform — high-performance JS animation engine with plugin ecosystem.

Install

npm install gsap
# Plugins (ScrollTrigger free, others require club membership):
# ScrollTrigger, Flip, Observer, ScrollSmoother — included in gsap package

Core API

import gsap from "gsap";
import { ScrollTrigger } from "gsap/ScrollTrigger";
import { Flip } from "gsap/Flip";
gsap.registerPlugin(ScrollTrigger, Flip);
MethodPurpose
gsap.to(target, vars)Animate FROM current state TO vars
gsap.from(target, vars)Animate FROM vars TO current state
gsap.fromTo(target, from, to)Explicit start and end
gsap.set(target, vars)Instant set, no animation
gsap.timeline(vars)Sequence multiple tweens
gsap.context(fn, scope)Scope cleanup (React)
gsap.matchMedia()Responsive animations

Tween Properties

gsap.to(".box", {
  x: 100,           // translateX (px)
  y: -50,           // translateY
  rotation: 360,    // degrees
  scale: 1.5,
  opacity: 0,
  duration: 1,
  delay: 0.2,
  ease: "power2.out",
  stagger: 0.1,     // when targeting multiple elements
  repeat: -1,       // -1 = infinite
  yoyo: true,       // reverse on repeat
  onComplete: () => console.log("done"),
});

Easing Reference

EaseCharacter
power1-4.in/out/inOutPolynomial — power2.out is the go-to
elastic.out(1, 0.3)Springy bounce
back.out(1.7)Slight overshoot
bounce.outBall bounce
circ.outCircular — sharp deceleration
expo.outDramatic fast-to-slow
none / linearConstant speed

Timeline

const tl = gsap.timeline({ defaults: { ease: "power2.out", duration: 0.5 } });

tl.from(".hero-title",    { opacity: 0, y: 60 })
  .from(".hero-subtitle", { opacity: 0, y: 40 }, "-=0.3")   // 0.3s overlap
  .from(".hero-cta",      { opacity: 0, scale: 0.9 }, "+=0.1") // 0.1s gap
  .from(".hero-image",    { opacity: 0, x: 60 }, "<0.2");    // same start as prev + 0.2

// Position parameter:
// "<"   = same start as previous
// ">"   = end of previous
// "+=n" = n seconds after end
// "-=n" = n seconds before end
// "1.5" = absolute time in timeline

ScrollTrigger

gsap.to(".panel", {
  xPercent: -100 * (panels.length - 1),
  ease: "none",
  scrollTrigger: {
    trigger: ".scroll-container",
    pin: true,           // pins the trigger element during scroll
    scrub: 1,            // smooth scrubbing (lag in seconds)
    snap: 1 / (panels.length - 1),
    end: () => `+=${containerRef.current?.offsetWidth}`,
  }
});

// Callbacks
ScrollTrigger.create({
  trigger: "#section",
  start: "top 80%",
  end: "bottom 20%",
  onEnter: () => console.log("entered"),
  onLeave: () => console.log("left"),
  onEnterBack: () => console.log("entered back"),
  markers: process.env.NODE_ENV === "development", // visual debug markers
});

React Integration — useGSAP hook

import { useGSAP } from "@gsap/react";
import gsap from "gsap";
import { useRef } from "react";

gsap.registerPlugin(useGSAP);

function AnimatedHero() {
  const containerRef = useRef<HTMLDivElement>(null);

  useGSAP(() => {
    // All GSAP code here is automatically cleaned up on unmount
    gsap.from(".title", { opacity: 0, y: 60, duration: 0.8, ease: "power3.out" });
  }, { scope: containerRef }); // scope = only select within containerRef

  return (
    <div ref={containerRef}>
      <h1 className="title">Hello</h1>
    </div>
  );
}

Context (manual cleanup without useGSAP)

useEffect(() => {
  const ctx = gsap.context(() => {
    gsap.from(".item", { opacity: 0, stagger: 0.1 });
  }, containerRef);

  return () => ctx.revert(); // kills all tweens created in context
}, []);

Stagger

gsap.from(".card", {
  opacity: 0, y: 30,
  stagger: {
    amount: 0.6,         // total time spread across all elements
    from: "center",      // start: "start" | "end" | "center" | "random" | index
    ease: "power2.inOut",
    grid: [3, 4],        // for grid layouts
  }
});

SVG Path Draw (manual, no plugin)

function animatePath(el: SVGPathElement) {
  const len = el.getTotalLength();
  gsap.set(el, { strokeDasharray: len, strokeDashoffset: len });
  gsap.to(el, { strokeDashoffset: 0, duration: 2, ease: "power2.inOut" });
}

matchMedia (responsive)

const mm = gsap.matchMedia();

mm.add("(min-width: 768px)", () => {
  gsap.from(".title", { x: -100, opacity: 0 });
});

mm.add("(max-width: 767px)", () => {
  gsap.from(".title", { y: 40, opacity: 0 });
});

// Cleanup:
return () => mm.revert();

Reduced Motion

const prefersReduced = window.matchMedia("(prefers-reduced-motion: reduce)").matches;

gsap.to(".element", {
  x: prefersReduced ? 0 : 200,
  duration: prefersReduced ? 0 : 1,
  opacity: 1,
});

Pitfalls

  • Always ctx.revert() or use useGSAP in React — GSAP tweens survive component unmount otherwise
  • scrub: true = hard sync with scroll; scrub: 1 = 1s lag (smoother)
  • pin: true — the trigger element must have explicit height for the pinned section to work
  • Avoid document.querySelector inside components — use refs with gsap.context(scope)
  • ScrollTrigger.refresh() after dynamic content loads (images, fonts)

Signals

GitHub stars
33
Forks
6
Last commit
Sep 2026
Hacker News mentions
9
Advanced
Catalog kind
skill
Gateway key
gsap-claude-dev-suite
Source
github.com/claude-dev-suite/claude-dev-suite