Expert knowledge for GSAP (GreenSock Animation Platform) - the industry-standard JavaScript animation library for professional-grade animations with precise timeline control.
From react-animation-studionpx claudepluginhub markus41/claude --plugin react-animation-studioThis skill uses the workspace's default tool permissions.
Guides Next.js Cache Components and Partial Prerendering (PPR) with cacheComponents enabled. Implements 'use cache', cacheLife(), cacheTag(), revalidateTag(), static/dynamic optimization, and cache debugging.
Migrates code, prompts, and API calls from Claude Sonnet 4.0/4.5 or Opus 4.1 to Opus 4.5, updating model strings on Anthropic, AWS, GCP, Azure platforms.
Details PluginEval's skill quality evaluation: 3 layers (static, LLM judge), 10 dimensions, rubrics, formulas, anti-patterns, badges. Use to interpret scores, improve triggering, calibrate thresholds.
Expert knowledge for GSAP (GreenSock Animation Platform) - the industry-standard JavaScript animation library for professional-grade animations with precise timeline control.
Activate this skill when:
**/*.tsx containing gsap imports**/*.ts with timeline animations**/animations/*.ts with gsap configurations# Core library
npm install gsap
# With React integration
npm install gsap @gsap/react
# Specific plugins (if needed)
npm install gsap/ScrollTrigger
import gsap from 'gsap';
// To animation (animate TO these values)
gsap.to('.element', {
x: 100,
opacity: 1,
duration: 1,
ease: 'power2.out',
});
// From animation (animate FROM these values)
gsap.from('.element', {
x: -100,
opacity: 0,
duration: 1,
});
// FromTo animation (both start and end)
gsap.fromTo('.element',
{ x: -100, opacity: 0 },
{ x: 0, opacity: 1, duration: 1 }
);
// Set (instant, no animation)
gsap.set('.element', { x: 0, opacity: 1 });
const tl = gsap.timeline({
defaults: { duration: 0.5, ease: 'power2.out' },
});
tl.to('.header', { y: 0, opacity: 1 })
.to('.nav-item', { y: 0, opacity: 1, stagger: 0.1 }, '-=0.3')
.to('.content', { y: 0, opacity: 1 }, '<0.2') // 0.2s after previous starts
.to('.footer', { y: 0, opacity: 1 }, '>-0.1'); // 0.1s before previous ends
tl.to(el, { x: 100 }) // End of timeline
.to(el, { y: 100 }, '+=0.5') // 0.5s gap after previous
.to(el, { z: 100 }, '-=0.25') // 0.25s overlap
.to(el, { rotation: 90 }, '<') // Same time as previous
.to(el, { scale: 2 }, '<0.5') // 0.5s after previous starts
.to(el, { opacity: 0 }, 2) // Absolute time: 2s from start
.to(el, { color: 'red' }, 'myLabel') // At label
.to(el, { width: 200 }, 'myLabel+=1'); // 1s after label
import { useGSAP } from '@gsap/react';
import gsap from 'gsap';
function Component() {
const containerRef = useRef<HTMLDivElement>(null);
useGSAP(() => {
// GSAP animations here - auto-cleanup on unmount
gsap.to('.box', {
x: 200,
rotation: 360,
duration: 2,
ease: 'elastic.out(1, 0.3)',
});
}, { scope: containerRef }); // Scope selector queries to container
return (
<div ref={containerRef}>
<div className="box">Animated</div>
</div>
);
}
import { useRef, useEffect } from 'react';
import gsap from 'gsap';
function Component() {
const elementRef = useRef<HTMLDivElement>(null);
useEffect(() => {
const ctx = gsap.context(() => {
gsap.to(elementRef.current, {
x: 100,
duration: 1,
});
});
return () => ctx.revert(); // Cleanup
}, []);
return <div ref={elementRef}>Animated</div>;
}
import gsap from 'gsap';
import { ScrollTrigger } from 'gsap/ScrollTrigger';
gsap.registerPlugin(ScrollTrigger);
// Basic scroll-triggered animation
gsap.to('.element', {
x: 500,
scrollTrigger: {
trigger: '.element',
start: 'top 80%', // When top of element hits 80% of viewport
end: 'bottom 20%', // When bottom of element hits 20% of viewport
scrub: true, // Link to scroll position
markers: true, // Debug markers (dev only)
},
});
// Timeline with ScrollTrigger
const tl = gsap.timeline({
scrollTrigger: {
trigger: '.section',
start: 'top top',
end: '+=300%',
scrub: 1, // Smooth scrubbing (1 second lag)
pin: true, // Pin the element
anticipatePin: 1, // Prevent jank on pin
},
});
tl.to('.panel-1', { xPercent: -100 })
.to('.panel-2', { xPercent: -100 })
.to('.panel-3', { xPercent: -100 });
// Built-in eases
ease: 'none' // Linear
ease: 'power1.out' // Subtle ease out
ease: 'power2.inOut' // Moderate ease in-out
ease: 'power3.out' // Strong ease out
ease: 'power4.in' // Very strong ease in
ease: 'back.out(1.7)' // Overshoot
ease: 'elastic.out(1, 0.3)' // Bouncy spring
ease: 'bounce.out' // Bounce
ease: 'circ.out' // Circular
ease: 'expo.out' // Exponential
ease: 'sine.inOut' // Sine wave
// Custom ease
ease: 'M0,0 C0.126,0.382 0.282,0.674 0.44,0.822 0.632,1.002 0.818,1.001 1,1'
// Simple stagger
gsap.to('.item', {
y: 0,
opacity: 1,
stagger: 0.1, // 0.1s between each
});
// Advanced stagger
gsap.to('.grid-item', {
scale: 1,
opacity: 1,
stagger: {
amount: 1, // Total stagger time
from: 'center', // 'start', 'end', 'center', 'edges', 'random', or index
grid: [5, 5], // For grid layouts
ease: 'power2.out',
axis: 'x', // Stagger along x-axis only
},
});
// Function-based stagger
gsap.to('.item', {
y: 0,
stagger: (index, target, list) => {
return index * 0.1 + Math.random() * 0.1;
},
});
{
x: 100, // translateX (px or %)
y: 100, // translateY
xPercent: -50, // translateX as percentage of element width
yPercent: -50, // translateY as percentage of element height
rotation: 360, // rotate (degrees)
rotationX: 45, // 3D rotation
rotationY: 45, // 3D rotation
scale: 1.5, // uniform scale
scaleX: 2, // scale X
scaleY: 2, // scale Y
skewX: 45, // skew X
skewY: 45, // skew Y
transformOrigin: '50% 50%',
transformPerspective: 1000,
}
{
autoAlpha: 1, // opacity + visibility
immediateRender: false,
overwrite: 'auto', // Handle conflicting tweens
repeat: -1, // Infinite repeat
repeatDelay: 0.5,
yoyo: true, // Alternate direction on repeat
reversed: true, // Start reversed
paused: true, // Start paused
}
gsap.utils.toArray('.reveal').forEach((element) => {
gsap.from(element as Element, {
y: 60,
opacity: 0,
duration: 1,
ease: 'power3.out',
scrollTrigger: {
trigger: element as Element,
start: 'top 85%',
toggleActions: 'play none none reverse',
},
});
});
const sections = gsap.utils.toArray('.panel');
const totalWidth = sections.length * window.innerWidth;
gsap.to(sections, {
xPercent: -100 * (sections.length - 1),
ease: 'none',
scrollTrigger: {
trigger: '.container',
pin: true,
scrub: 1,
snap: 1 / (sections.length - 1),
end: () => '+=' + totalWidth,
},
});
// With SplitText plugin or manual split
const chars = text.split('');
element.innerHTML = chars.map(c => `<span class="char">${c}</span>`).join('');
gsap.from('.char', {
opacity: 0,
y: 50,
rotateX: -90,
stagger: 0.02,
duration: 0.5,
ease: 'back.out(1.7)',
});
gsap.registerPlugin(ScrollTrigger);
function CardSection() { const containerRef = useRef(null);
useGSAP(() => { gsap.from('.card', { y: 80, opacity: 0, duration: 0.8, ease: 'power3.out', stagger: 0.15, scrollTrigger: { trigger: '.cards-container', start: 'top 75%', }, }); }, { scope: containerRef });
return ( <div ref={containerRef}> <div className="cards-container"> {cards.map(card => <div className="card" key={card.id}>{card.content}</div>)} </div> </div> ); }
</example>
<example>
Context: User needs a complex loading animation sequence
user: "Create a logo animation where parts reveal sequentially"
assistant: Using GSAP timeline for orchestrated sequence:
```typescript
const logoAnimation = gsap.timeline({ paused: true });
logoAnimation
.from('.logo-icon', {
scale: 0,
rotation: -180,
duration: 0.6,
ease: 'back.out(1.7)',
})
.from('.logo-text span', {
opacity: 0,
y: 30,
stagger: 0.05,
duration: 0.4,
ease: 'power2.out',
}, '-=0.2')
.from('.tagline', {
opacity: 0,
y: 20,
duration: 0.4,
}, '-=0.1');
// Play when ready
logoAnimation.play();
</example>
mcp__plugin_context7_context7__query-docs with gsap for latest APICreated by Brookside BI as part of React Animation Studio