tribunal-kit 2.4.6 → 3.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.agent/agents/accessibility-reviewer.md +220 -134
- package/.agent/agents/ai-code-reviewer.md +233 -129
- package/.agent/agents/backend-specialist.md +238 -178
- package/.agent/agents/code-archaeologist.md +181 -119
- package/.agent/agents/database-architect.md +207 -164
- package/.agent/agents/debugger.md +218 -151
- package/.agent/agents/dependency-reviewer.md +136 -55
- package/.agent/agents/devops-engineer.md +238 -175
- package/.agent/agents/documentation-writer.md +221 -137
- package/.agent/agents/explorer-agent.md +180 -142
- package/.agent/agents/frontend-reviewer.md +194 -80
- package/.agent/agents/frontend-specialist.md +237 -188
- package/.agent/agents/game-developer.md +52 -184
- package/.agent/agents/logic-reviewer.md +149 -78
- package/.agent/agents/mobile-developer.md +223 -152
- package/.agent/agents/mobile-reviewer.md +195 -79
- package/.agent/agents/orchestrator.md +211 -170
- package/.agent/agents/penetration-tester.md +174 -131
- package/.agent/agents/performance-optimizer.md +203 -139
- package/.agent/agents/performance-reviewer.md +211 -108
- package/.agent/agents/product-manager.md +162 -108
- package/.agent/agents/project-planner.md +162 -142
- package/.agent/agents/qa-automation-engineer.md +242 -138
- package/.agent/agents/security-auditor.md +194 -170
- package/.agent/agents/seo-specialist.md +213 -132
- package/.agent/agents/sql-reviewer.md +194 -73
- package/.agent/agents/supervisor-agent.md +203 -156
- package/.agent/agents/test-coverage-reviewer.md +193 -81
- package/.agent/agents/type-safety-reviewer.md +208 -65
- package/.agent/scripts/__pycache__/auto_preview.cpython-311.pyc +0 -0
- package/.agent/scripts/__pycache__/bundle_analyzer.cpython-311.pyc +0 -0
- package/.agent/scripts/__pycache__/checklist.cpython-311.pyc +0 -0
- package/.agent/scripts/__pycache__/dependency_analyzer.cpython-311.pyc +0 -0
- package/.agent/scripts/__pycache__/security_scan.cpython-311.pyc +0 -0
- package/.agent/scripts/__pycache__/session_manager.cpython-311.pyc +0 -0
- package/.agent/scripts/__pycache__/skill_integrator.cpython-311.pyc +0 -0
- package/.agent/scripts/__pycache__/swarm_dispatcher.cpython-311.pyc +0 -0
- package/.agent/scripts/__pycache__/test_runner.cpython-311.pyc +0 -0
- package/.agent/scripts/__pycache__/verify_all.cpython-311.pyc +0 -0
- package/.agent/skills/agent-organizer/SKILL.md +126 -132
- package/.agent/skills/ai-prompt-injection-defense/SKILL.md +155 -66
- package/.agent/skills/api-patterns/SKILL.md +289 -257
- package/.agent/skills/api-security-auditor/SKILL.md +172 -70
- package/.agent/skills/app-builder/templates/chrome-extension/TEMPLATE.md +1 -1
- package/.agent/skills/app-builder/templates/electron-desktop/TEMPLATE.md +1 -1
- package/.agent/skills/appflow-wireframe/SKILL.md +107 -100
- package/.agent/skills/architecture/SKILL.md +331 -200
- package/.agent/skills/authentication-best-practices/SKILL.md +168 -67
- package/.agent/skills/bash-linux/SKILL.md +154 -215
- package/.agent/skills/brainstorming/SKILL.md +104 -210
- package/.agent/skills/building-native-ui/SKILL.md +169 -70
- package/.agent/skills/clean-code/SKILL.md +360 -206
- package/.agent/skills/config-validator/SKILL.md +141 -165
- package/.agent/skills/csharp-developer/SKILL.md +528 -107
- package/.agent/skills/database-design/SKILL.md +455 -275
- package/.agent/skills/deployment-procedures/SKILL.md +145 -188
- package/.agent/skills/devops-engineer/SKILL.md +332 -134
- package/.agent/skills/devops-incident-responder/SKILL.md +113 -98
- package/.agent/skills/edge-computing/SKILL.md +157 -213
- package/.agent/skills/extract-design-system/SKILL.md +129 -69
- package/.agent/skills/framer-motion-expert/SKILL.md +939 -0
- package/.agent/skills/game-design-expert/SKILL.md +105 -0
- package/.agent/skills/game-engineering-expert/SKILL.md +122 -0
- package/.agent/skills/geo-fundamentals/SKILL.md +124 -215
- package/.agent/skills/github-operations/SKILL.md +314 -354
- package/.agent/skills/gsap-expert/SKILL.md +901 -0
- package/.agent/skills/i18n-localization/SKILL.md +138 -216
- package/.agent/skills/intelligent-routing/SKILL.md +127 -139
- package/.agent/skills/llm-engineering/SKILL.md +357 -258
- package/.agent/skills/local-first/SKILL.md +154 -203
- package/.agent/skills/mcp-builder/SKILL.md +118 -224
- package/.agent/skills/nextjs-react-expert/SKILL.md +783 -203
- package/.agent/skills/nodejs-best-practices/SKILL.md +559 -280
- package/.agent/skills/observability/SKILL.md +330 -285
- package/.agent/skills/parallel-agents/SKILL.md +122 -181
- package/.agent/skills/performance-profiling/SKILL.md +254 -197
- package/.agent/skills/plan-writing/SKILL.md +118 -188
- package/.agent/skills/platform-engineer/SKILL.md +123 -135
- package/.agent/skills/playwright-best-practices/SKILL.md +157 -76
- package/.agent/skills/powershell-windows/SKILL.md +146 -230
- package/.agent/skills/python-pro/SKILL.md +879 -114
- package/.agent/skills/react-specialist/SKILL.md +931 -108
- package/.agent/skills/realtime-patterns/SKILL.md +304 -296
- package/.agent/skills/rust-pro/SKILL.md +701 -240
- package/.agent/skills/seo-fundamentals/SKILL.md +154 -181
- package/.agent/skills/server-management/SKILL.md +190 -212
- package/.agent/skills/shadcn-ui-expert/SKILL.md +201 -68
- package/.agent/skills/sql-pro/SKILL.md +633 -104
- package/.agent/skills/swiftui-expert/SKILL.md +171 -70
- package/.agent/skills/systematic-debugging/SKILL.md +118 -186
- package/.agent/skills/tailwind-patterns/SKILL.md +576 -232
- package/.agent/skills/tdd-workflow/SKILL.md +137 -209
- package/.agent/skills/testing-patterns/SKILL.md +573 -205
- package/.agent/skills/vue-expert/SKILL.md +964 -119
- package/.agent/skills/vulnerability-scanner/SKILL.md +269 -316
- package/.agent/skills/web-accessibility-auditor/SKILL.md +188 -71
- package/.agent/skills/webapp-testing/SKILL.md +145 -236
- package/.agent/workflows/api-tester.md +151 -279
- package/.agent/workflows/audit.md +138 -168
- package/.agent/workflows/brainstorm.md +110 -146
- package/.agent/workflows/changelog.md +112 -144
- package/.agent/workflows/create.md +124 -139
- package/.agent/workflows/debug.md +189 -196
- package/.agent/workflows/deploy.md +189 -153
- package/.agent/workflows/enhance.md +151 -139
- package/.agent/workflows/fix.md +135 -143
- package/.agent/workflows/generate.md +157 -164
- package/.agent/workflows/migrate.md +160 -163
- package/.agent/workflows/orchestrate.md +168 -151
- package/.agent/workflows/performance-benchmarker.md +123 -305
- package/.agent/workflows/plan.md +173 -151
- package/.agent/workflows/preview.md +80 -137
- package/.agent/workflows/refactor.md +183 -153
- package/.agent/workflows/review-ai.md +129 -140
- package/.agent/workflows/review.md +116 -155
- package/.agent/workflows/session.md +94 -154
- package/.agent/workflows/status.md +79 -125
- package/.agent/workflows/strengthen-skills.md +139 -99
- package/.agent/workflows/swarm.md +179 -194
- package/.agent/workflows/test.md +211 -166
- package/.agent/workflows/tribunal-backend.md +113 -111
- package/.agent/workflows/tribunal-database.md +115 -132
- package/.agent/workflows/tribunal-frontend.md +118 -115
- package/.agent/workflows/tribunal-full.md +133 -136
- package/.agent/workflows/tribunal-mobile.md +119 -123
- package/.agent/workflows/tribunal-performance.md +133 -152
- package/.agent/workflows/ui-ux-pro-max.md +143 -171
- package/README.md +11 -15
- package/package.json +1 -1
- package/.agent/skills/dotnet-core-expert/SKILL.md +0 -103
- package/.agent/skills/framer-motion-animations/SKILL.md +0 -74
- package/.agent/skills/game-development/2d-games/SKILL.md +0 -119
- package/.agent/skills/game-development/3d-games/SKILL.md +0 -135
- package/.agent/skills/game-development/SKILL.md +0 -236
- package/.agent/skills/game-development/game-art/SKILL.md +0 -185
- package/.agent/skills/game-development/game-audio/SKILL.md +0 -190
- package/.agent/skills/game-development/game-design/SKILL.md +0 -129
- package/.agent/skills/game-development/mobile-games/SKILL.md +0 -108
- package/.agent/skills/game-development/multiplayer/SKILL.md +0 -132
- package/.agent/skills/game-development/pc-games/SKILL.md +0 -144
- package/.agent/skills/game-development/vr-ar/SKILL.md +0 -123
- package/.agent/skills/game-development/web-games/SKILL.md +0 -150
|
@@ -0,0 +1,901 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: gsap-expert
|
|
3
|
+
description: GreenSock Animation Platform (GSAP 3.12+) mastery. Core tweens, timelines, ScrollTrigger, plugins, gsap.utils, React useGSAP hook, performance optimization, and multi-framework lifecycle cleanup. Use when building scroll-driven animations, complex sequencing, SVG morphing, or any animation beyond CSS capabilities.
|
|
4
|
+
allowed-tools: Read, Write, Edit, Glob, Grep
|
|
5
|
+
version: 1.0.0
|
|
6
|
+
last-updated: 2026-03-30
|
|
7
|
+
applies-to-model: gemini-2.5-pro, claude-3-7-sonnet
|
|
8
|
+
---
|
|
9
|
+
|
|
10
|
+
# GSAP Expert — GreenSock Animation Platform
|
|
11
|
+
|
|
12
|
+
> GSAP is the professional-grade animation library. It is NOT jQuery `.animate()`.
|
|
13
|
+
> Every tween must have a clear purpose, every timeline must clean up, and every ScrollTrigger must call `.kill()` on unmount.
|
|
14
|
+
|
|
15
|
+
---
|
|
16
|
+
|
|
17
|
+
## gsap-core — Core API
|
|
18
|
+
|
|
19
|
+
### Tween Methods
|
|
20
|
+
|
|
21
|
+
```js
|
|
22
|
+
// gsap.to() — animate FROM current state TO target
|
|
23
|
+
gsap.to(".box", { x: 200, duration: 1, ease: "power2.out" });
|
|
24
|
+
|
|
25
|
+
// gsap.from() — animate FROM target TO current state
|
|
26
|
+
gsap.from(".box", { opacity: 0, y: 50, duration: 0.8 });
|
|
27
|
+
|
|
28
|
+
// gsap.fromTo() — explicit start AND end (most predictable)
|
|
29
|
+
gsap.fromTo(".box",
|
|
30
|
+
{ opacity: 0, y: 30 }, // from
|
|
31
|
+
{ opacity: 1, y: 0, duration: 1 } // to
|
|
32
|
+
);
|
|
33
|
+
|
|
34
|
+
// gsap.set() — instant property set, no animation
|
|
35
|
+
gsap.set(".box", { transformOrigin: "center center" });
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
### Easing
|
|
39
|
+
|
|
40
|
+
GSAP eases follow a `name.type` pattern. The names: `power1`–`power4`, `back`, `bounce`, `elastic`, `circ`, `expo`, `sine`, `steps`.
|
|
41
|
+
|
|
42
|
+
```js
|
|
43
|
+
// Types: .in, .out, .inOut
|
|
44
|
+
ease: "power3.inOut" // smooth acceleration + deceleration
|
|
45
|
+
ease: "back.out(1.7)" // overshoot by 1.7 (default)
|
|
46
|
+
ease: "elastic.out(1, 0.3)" // amplitude, period
|
|
47
|
+
ease: "steps(5)" // stepped animation (sprite sheets)
|
|
48
|
+
ease: "none" // linear
|
|
49
|
+
|
|
50
|
+
// ❌ HALLUCINATION TRAP: These do NOT exist in GSAP 3+
|
|
51
|
+
// ease: "easeInOut" — jQuery syntax, NOT GSAP
|
|
52
|
+
// ease: "Power2.easeOut" — GSAP 2 syntax, DEPRECATED
|
|
53
|
+
// ease: "Cubic.easeIn" — GSAP 2 syntax, DEPRECATED
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
### Duration & Stagger
|
|
57
|
+
|
|
58
|
+
```js
|
|
59
|
+
// Duration is in seconds (NOT milliseconds like CSS/JS setTimeout)
|
|
60
|
+
gsap.to(".items", {
|
|
61
|
+
x: 100,
|
|
62
|
+
duration: 0.6, // 0.6 seconds
|
|
63
|
+
stagger: 0.1, // 0.1s between each element
|
|
64
|
+
|
|
65
|
+
// Advanced stagger object
|
|
66
|
+
stagger: {
|
|
67
|
+
each: 0.1, // time between each
|
|
68
|
+
from: "center", // "start", "center", "end", "edges", "random", or index
|
|
69
|
+
grid: "auto", // for grid layouts
|
|
70
|
+
ease: "power2.in", // ease the stagger distribution itself
|
|
71
|
+
amount: 0.8, // total stagger time (alternative to `each`)
|
|
72
|
+
}
|
|
73
|
+
});
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
### Defaults
|
|
77
|
+
|
|
78
|
+
```js
|
|
79
|
+
// Set project-wide defaults — DRY animation config
|
|
80
|
+
gsap.defaults({
|
|
81
|
+
duration: 0.8,
|
|
82
|
+
ease: "power2.out",
|
|
83
|
+
overwrite: "auto", // auto-kill conflicting tweens on same target
|
|
84
|
+
});
|
|
85
|
+
|
|
86
|
+
// Per-tween overrides always win
|
|
87
|
+
gsap.to(".box", { x: 100, duration: 1.5 }); // uses 1.5, not 0.8
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
### Callbacks & Control
|
|
91
|
+
|
|
92
|
+
```js
|
|
93
|
+
const tween = gsap.to(".box", {
|
|
94
|
+
x: 200,
|
|
95
|
+
onStart: () => console.log("started"),
|
|
96
|
+
onUpdate: (self) => console.log(self.progress()),
|
|
97
|
+
onComplete: () => console.log("done"),
|
|
98
|
+
onReverseComplete: () => console.log("reversed"),
|
|
99
|
+
});
|
|
100
|
+
|
|
101
|
+
// Control methods
|
|
102
|
+
tween.pause();
|
|
103
|
+
tween.resume();
|
|
104
|
+
tween.reverse();
|
|
105
|
+
tween.seek(0.5); // jump to 0.5 seconds
|
|
106
|
+
tween.progress(0.5); // jump to 50%
|
|
107
|
+
tween.kill(); // destroy and garbage collect
|
|
108
|
+
```
|
|
109
|
+
|
|
110
|
+
---
|
|
111
|
+
|
|
112
|
+
## gsap-timeline — Timelines
|
|
113
|
+
|
|
114
|
+
### Sequencing
|
|
115
|
+
|
|
116
|
+
```js
|
|
117
|
+
const tl = gsap.timeline({
|
|
118
|
+
defaults: { duration: 0.5, ease: "power2.out" },
|
|
119
|
+
onComplete: () => console.log("Sequence done"),
|
|
120
|
+
});
|
|
121
|
+
|
|
122
|
+
tl.to(".title", { opacity: 1, y: 0 })
|
|
123
|
+
.to(".subtitle", { opacity: 1, y: 0 }) // plays AFTER .title
|
|
124
|
+
.to(".cta", { scale: 1, opacity: 1 }); // plays AFTER .subtitle
|
|
125
|
+
```
|
|
126
|
+
|
|
127
|
+
### Position Parameter (Critical Concept)
|
|
128
|
+
|
|
129
|
+
The position parameter is the **most powerful** sequencing tool in GSAP. It controls *when* a child animation starts relative to the timeline.
|
|
130
|
+
|
|
131
|
+
```js
|
|
132
|
+
tl.to(".a", { x: 100 })
|
|
133
|
+
.to(".b", { x: 100 }, "<") // "<" = same start time as previous
|
|
134
|
+
.to(".c", { x: 100 }, ">") // ">" = after previous ends (default)
|
|
135
|
+
.to(".d", { x: 100 }, "-=0.3") // 0.3s before previous ends (overlap)
|
|
136
|
+
.to(".e", { x: 100 }, "+=0.5") // 0.5s after previous ends (gap)
|
|
137
|
+
.to(".f", { x: 100 }, 2) // absolute: at exactly 2 seconds
|
|
138
|
+
.to(".g", { x: 100 }, "<0.2") // 0.2s after previous animation's START
|
|
139
|
+
.to(".h", { x: 100 }, ">-0.1"); // 0.1s before previous animation's END
|
|
140
|
+
|
|
141
|
+
// ❌ HALLUCINATION TRAP: Position parameter is the 3rd argument, NOT a property
|
|
142
|
+
// WRONG: tl.to(".a", { x: 100, position: "<" })
|
|
143
|
+
// RIGHT: tl.to(".a", { x: 100 }, "<")
|
|
144
|
+
```
|
|
145
|
+
|
|
146
|
+
### Labels
|
|
147
|
+
|
|
148
|
+
```js
|
|
149
|
+
tl.addLabel("intro")
|
|
150
|
+
.to(".title", { opacity: 1 })
|
|
151
|
+
.addLabel("middle")
|
|
152
|
+
.to(".content", { opacity: 1 })
|
|
153
|
+
.to(".sidebar", { x: 0 }, "middle") // starts at the "middle" label
|
|
154
|
+
.to(".footer", { y: 0 }, "middle+=0.3"); // 0.3s after the "middle" label
|
|
155
|
+
```
|
|
156
|
+
|
|
157
|
+
### Nesting Timelines
|
|
158
|
+
|
|
159
|
+
```js
|
|
160
|
+
function introAnimation() {
|
|
161
|
+
const tl = gsap.timeline();
|
|
162
|
+
tl.from(".hero-title", { y: 60, opacity: 0 })
|
|
163
|
+
.from(".hero-sub", { y: 40, opacity: 0 }, "-=0.3");
|
|
164
|
+
return tl;
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
function contentAnimation() {
|
|
168
|
+
const tl = gsap.timeline();
|
|
169
|
+
tl.from(".card", { y: 80, opacity: 0, stagger: 0.15 });
|
|
170
|
+
return tl;
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
// Master timeline nests sub-timelines
|
|
174
|
+
const master = gsap.timeline();
|
|
175
|
+
master
|
|
176
|
+
.add(introAnimation())
|
|
177
|
+
.add(contentAnimation(), "-=0.4"); // overlap by 0.4s
|
|
178
|
+
```
|
|
179
|
+
|
|
180
|
+
### Playback Control
|
|
181
|
+
|
|
182
|
+
```js
|
|
183
|
+
const tl = gsap.timeline({ paused: true, repeat: -1, yoyo: true });
|
|
184
|
+
|
|
185
|
+
tl.play();
|
|
186
|
+
tl.pause();
|
|
187
|
+
tl.reverse();
|
|
188
|
+
tl.restart();
|
|
189
|
+
tl.timeScale(2); // 2× speed
|
|
190
|
+
tl.seek("middle"); // jump to label
|
|
191
|
+
tl.totalProgress(); // 0–1 across all repeats
|
|
192
|
+
```
|
|
193
|
+
|
|
194
|
+
---
|
|
195
|
+
|
|
196
|
+
## gsap-scrolltrigger — ScrollTrigger
|
|
197
|
+
|
|
198
|
+
### Registration (Required)
|
|
199
|
+
|
|
200
|
+
```js
|
|
201
|
+
import { gsap } from "gsap";
|
|
202
|
+
import { ScrollTrigger } from "gsap/ScrollTrigger";
|
|
203
|
+
|
|
204
|
+
gsap.registerPlugin(ScrollTrigger);
|
|
205
|
+
|
|
206
|
+
// ❌ HALLUCINATION TRAP: ScrollTrigger MUST be registered before use
|
|
207
|
+
// Forgetting gsap.registerPlugin() is the #1 GSAP bug in AI-generated code
|
|
208
|
+
```
|
|
209
|
+
|
|
210
|
+
### Basic Scroll-Linked Animation
|
|
211
|
+
|
|
212
|
+
```js
|
|
213
|
+
gsap.to(".parallax-bg", {
|
|
214
|
+
y: -200,
|
|
215
|
+
scrollTrigger: {
|
|
216
|
+
trigger: ".hero-section", // element that triggers
|
|
217
|
+
start: "top bottom", // "triggerPoint viewportPoint"
|
|
218
|
+
end: "bottom top", // when to stop
|
|
219
|
+
scrub: true, // links animation progress to scroll position
|
|
220
|
+
markers: true, // DEBUG ONLY — remove in production
|
|
221
|
+
}
|
|
222
|
+
});
|
|
223
|
+
```
|
|
224
|
+
|
|
225
|
+
### Start / End Syntax
|
|
226
|
+
|
|
227
|
+
```
|
|
228
|
+
start: "top center" // trigger's top hits viewport's center
|
|
229
|
+
start: "top 80%" // trigger's top hits 80% from viewport top
|
|
230
|
+
start: "top top" // trigger's top hits viewport's top
|
|
231
|
+
start: "center center" // trigger's center hits viewport's center
|
|
232
|
+
start: "top bottom-=100" // 100px before trigger's top hits viewport's bottom
|
|
233
|
+
```
|
|
234
|
+
|
|
235
|
+
### Pinning
|
|
236
|
+
|
|
237
|
+
```js
|
|
238
|
+
ScrollTrigger.create({
|
|
239
|
+
trigger: ".sticky-section",
|
|
240
|
+
start: "top top",
|
|
241
|
+
end: "+=1000", // pin for 1000px of scrolling
|
|
242
|
+
pin: true, // locks element in place
|
|
243
|
+
pinSpacing: true, // adds equivalent space below (default: true)
|
|
244
|
+
anticipatePin: 1, // reduces jank — pre-calculates pin
|
|
245
|
+
});
|
|
246
|
+
```
|
|
247
|
+
|
|
248
|
+
### Scrub
|
|
249
|
+
|
|
250
|
+
```js
|
|
251
|
+
// scrub: true — instant scrub (direct 1:1 with scroll)
|
|
252
|
+
// scrub: 0.5 — smoothed scrub (0.5 second lag behind scroll position)
|
|
253
|
+
// scrub: 3 — heavy smoothing (3 second catch-up, cinematic feel)
|
|
254
|
+
|
|
255
|
+
gsap.to(".progress-bar", {
|
|
256
|
+
scaleX: 1,
|
|
257
|
+
scrollTrigger: {
|
|
258
|
+
trigger: ".article",
|
|
259
|
+
start: "top top",
|
|
260
|
+
end: "bottom bottom",
|
|
261
|
+
scrub: 0.3,
|
|
262
|
+
}
|
|
263
|
+
});
|
|
264
|
+
```
|
|
265
|
+
|
|
266
|
+
### Refresh & Cleanup (Critical)
|
|
267
|
+
|
|
268
|
+
```js
|
|
269
|
+
// Force recalculation after DOM changes (images loaded, content injected)
|
|
270
|
+
ScrollTrigger.refresh();
|
|
271
|
+
|
|
272
|
+
// Batch refresh after many DOM mutations
|
|
273
|
+
ScrollTrigger.refresh(true); // "safe" mode — deferred to next tick
|
|
274
|
+
|
|
275
|
+
// Kill ALL ScrollTriggers (page transitions, SPA navigation)
|
|
276
|
+
ScrollTrigger.killAll();
|
|
277
|
+
|
|
278
|
+
// Kill a specific instance
|
|
279
|
+
const st = ScrollTrigger.create({ /* ... */ });
|
|
280
|
+
st.kill();
|
|
281
|
+
|
|
282
|
+
// ❌ HALLUCINATION TRAP: Failing to kill ScrollTriggers on unmount
|
|
283
|
+
// causes memory leaks, zombie listeners, and broken scroll behavior
|
|
284
|
+
// in SPAs (Next.js, Nuxt, SvelteKit, Remix)
|
|
285
|
+
```
|
|
286
|
+
|
|
287
|
+
### Callbacks
|
|
288
|
+
|
|
289
|
+
```js
|
|
290
|
+
ScrollTrigger.create({
|
|
291
|
+
trigger: ".section",
|
|
292
|
+
start: "top center",
|
|
293
|
+
end: "bottom center",
|
|
294
|
+
onEnter: () => console.log("entered from above"),
|
|
295
|
+
onLeave: () => console.log("left going down"),
|
|
296
|
+
onEnterBack: () => console.log("entered from below"),
|
|
297
|
+
onLeaveBack: () => console.log("left going up"),
|
|
298
|
+
onToggle: (self) => console.log("active:", self.isActive),
|
|
299
|
+
onUpdate: (self) => console.log("progress:", self.progress),
|
|
300
|
+
onRefresh: (self) => console.log("recalculated"),
|
|
301
|
+
});
|
|
302
|
+
```
|
|
303
|
+
|
|
304
|
+
---
|
|
305
|
+
|
|
306
|
+
## gsap-plugins — Plugin Ecosystem
|
|
307
|
+
|
|
308
|
+
### Registration Pattern (All Plugins)
|
|
309
|
+
|
|
310
|
+
```js
|
|
311
|
+
import { gsap } from "gsap";
|
|
312
|
+
import { Flip } from "gsap/Flip";
|
|
313
|
+
import { Draggable } from "gsap/Draggable";
|
|
314
|
+
import { ScrollToPlugin } from "gsap/ScrollToPlugin";
|
|
315
|
+
|
|
316
|
+
gsap.registerPlugin(Flip, Draggable, ScrollToPlugin);
|
|
317
|
+
|
|
318
|
+
// ❌ HALLUCINATION TRAP: Every plugin MUST be registered
|
|
319
|
+
// ❌ HALLUCINATION TRAP: Some plugins require a GSAP Club license
|
|
320
|
+
// (ScrollSmoother, SplitText, Inertia, ScrambleText, MorphSVG, DrawSVG,
|
|
321
|
+
// MotionPath, Physics2D, PhysicsProps, CustomBounce, CustomWiggle)
|
|
322
|
+
// Free plugins: ScrollTrigger, Flip, Draggable, ScrollToPlugin, Observer,
|
|
323
|
+
// TextPlugin, MotionPathPlugin, EasePack, CustomEase, GSDevTools (trial)
|
|
324
|
+
```
|
|
325
|
+
|
|
326
|
+
### Flip Plugin (FLIP Technique)
|
|
327
|
+
|
|
328
|
+
```js
|
|
329
|
+
// 1. Capture current state
|
|
330
|
+
const state = Flip.getState(".cards");
|
|
331
|
+
|
|
332
|
+
// 2. Make DOM change (reparent, reclass, reorder)
|
|
333
|
+
container.classList.toggle("grid-layout");
|
|
334
|
+
|
|
335
|
+
// 3. Animate from old state to new state
|
|
336
|
+
Flip.from(state, {
|
|
337
|
+
duration: 0.6,
|
|
338
|
+
ease: "power2.inOut",
|
|
339
|
+
stagger: 0.05,
|
|
340
|
+
absolute: true, // uses position:absolute during animation
|
|
341
|
+
onComplete: () => console.log("Flip done"),
|
|
342
|
+
});
|
|
343
|
+
```
|
|
344
|
+
|
|
345
|
+
### Draggable
|
|
346
|
+
|
|
347
|
+
```js
|
|
348
|
+
Draggable.create(".slider-handle", {
|
|
349
|
+
type: "x", // "x", "y", "x,y", "rotation"
|
|
350
|
+
bounds: ".slider-track", // constrain to parent
|
|
351
|
+
inertia: true, // requires InertiaPlugin (Club)
|
|
352
|
+
edgeResistance: 0.65, // resistance at bounds edges
|
|
353
|
+
onDrag: function () {
|
|
354
|
+
console.log("x:", this.x); // `this` = Draggable instance
|
|
355
|
+
},
|
|
356
|
+
onDragEnd: function () {
|
|
357
|
+
console.log("endX:", this.endX); // projected landing position
|
|
358
|
+
},
|
|
359
|
+
snap: {
|
|
360
|
+
x: (val) => Math.round(val / 50) * 50, // snap to 50px grid
|
|
361
|
+
},
|
|
362
|
+
});
|
|
363
|
+
```
|
|
364
|
+
|
|
365
|
+
### ScrollToPlugin
|
|
366
|
+
|
|
367
|
+
```js
|
|
368
|
+
gsap.to(window, {
|
|
369
|
+
duration: 1,
|
|
370
|
+
scrollTo: { y: "#section-3", offsetY: 80 },
|
|
371
|
+
ease: "power2.inOut",
|
|
372
|
+
});
|
|
373
|
+
|
|
374
|
+
// Horizontal scroll-to
|
|
375
|
+
gsap.to(".container", {
|
|
376
|
+
scrollTo: { x: 500 },
|
|
377
|
+
duration: 0.8,
|
|
378
|
+
});
|
|
379
|
+
```
|
|
380
|
+
|
|
381
|
+
### ScrollSmoother (Club GreenSock)
|
|
382
|
+
|
|
383
|
+
```js
|
|
384
|
+
// ⚠️ Club GreenSock plugin — requires license
|
|
385
|
+
import { ScrollSmoother } from "gsap/ScrollSmoother";
|
|
386
|
+
|
|
387
|
+
// HTML structure required:
|
|
388
|
+
// <div id="smooth-wrapper">
|
|
389
|
+
// <div id="smooth-content"> ...page... </div>
|
|
390
|
+
// </div>
|
|
391
|
+
|
|
392
|
+
const smoother = ScrollSmoother.create({
|
|
393
|
+
wrapper: "#smooth-wrapper",
|
|
394
|
+
content: "#smooth-content",
|
|
395
|
+
smooth: 1.5, // seconds of smoothing
|
|
396
|
+
effects: true, // enables data-speed and data-lag
|
|
397
|
+
normalizeScroll: true, // prevents mobile address bar jank
|
|
398
|
+
});
|
|
399
|
+
```
|
|
400
|
+
|
|
401
|
+
### Observer
|
|
402
|
+
|
|
403
|
+
```js
|
|
404
|
+
import { Observer } from "gsap/Observer";
|
|
405
|
+
|
|
406
|
+
Observer.create({
|
|
407
|
+
target: window,
|
|
408
|
+
type: "wheel,touch,pointer",
|
|
409
|
+
onUp: () => goToNextSlide(),
|
|
410
|
+
onDown: () => goToPrevSlide(),
|
|
411
|
+
tolerance: 10, // pixel threshold before firing
|
|
412
|
+
preventDefault: true,
|
|
413
|
+
wheelSpeed: -1, // invert wheel direction
|
|
414
|
+
});
|
|
415
|
+
```
|
|
416
|
+
|
|
417
|
+
### SplitText (Club GreenSock)
|
|
418
|
+
|
|
419
|
+
```js
|
|
420
|
+
// ⚠️ Club GreenSock plugin — requires license
|
|
421
|
+
import { SplitText } from "gsap/SplitText";
|
|
422
|
+
|
|
423
|
+
const split = new SplitText(".hero-heading", {
|
|
424
|
+
type: "chars,words,lines",
|
|
425
|
+
linesClass: "split-line",
|
|
426
|
+
});
|
|
427
|
+
|
|
428
|
+
gsap.from(split.chars, {
|
|
429
|
+
opacity: 0,
|
|
430
|
+
y: 20,
|
|
431
|
+
rotateX: -60,
|
|
432
|
+
stagger: 0.02,
|
|
433
|
+
duration: 0.6,
|
|
434
|
+
ease: "back.out(1.7)",
|
|
435
|
+
onComplete: () => split.revert(), // CLEANUP — restore original DOM
|
|
436
|
+
});
|
|
437
|
+
|
|
438
|
+
// ❌ HALLUCINATION TRAP: Always call split.revert() when done
|
|
439
|
+
// Leaving split DOM nodes causes accessibility and SEO issues
|
|
440
|
+
```
|
|
441
|
+
|
|
442
|
+
### CustomEase
|
|
443
|
+
|
|
444
|
+
```js
|
|
445
|
+
import { CustomEase } from "gsap/CustomEase";
|
|
446
|
+
|
|
447
|
+
CustomEase.create("myBounce", "M0,0 C0.14,0 0.27,0.58 0.32,0.82 ...");
|
|
448
|
+
gsap.to(".box", { x: 300, ease: "myBounce" });
|
|
449
|
+
```
|
|
450
|
+
|
|
451
|
+
### GSDevTools
|
|
452
|
+
|
|
453
|
+
```js
|
|
454
|
+
// Debug timeline visually — NEVER ship to production
|
|
455
|
+
import { GSDevTools } from "gsap/GSDevTools";
|
|
456
|
+
|
|
457
|
+
GSDevTools.create({ animation: masterTimeline });
|
|
458
|
+
```
|
|
459
|
+
|
|
460
|
+
---
|
|
461
|
+
|
|
462
|
+
## gsap-utils — Utility Functions
|
|
463
|
+
|
|
464
|
+
```js
|
|
465
|
+
// clamp — constrain a value between min and max
|
|
466
|
+
gsap.utils.clamp(0, 100, 150); // 100
|
|
467
|
+
|
|
468
|
+
// mapRange — remap value from one range to another
|
|
469
|
+
gsap.utils.mapRange(0, 100, 0, 1, 50); // 0.5
|
|
470
|
+
|
|
471
|
+
// normalize — convert a value in a range to 0–1
|
|
472
|
+
gsap.utils.normalize(0, 500, 250); // 0.5
|
|
473
|
+
|
|
474
|
+
// interpolate — blend between values
|
|
475
|
+
gsap.utils.interpolate(0, 100, 0.75); // 75
|
|
476
|
+
gsap.utils.interpolate("red", "blue", 0.5); // blends colors
|
|
477
|
+
gsap.utils.interpolate({ x: 0 }, { x: 100 }, 0.5); // { x: 50 }
|
|
478
|
+
|
|
479
|
+
// random — random value (can be snapped)
|
|
480
|
+
gsap.utils.random(0, 100); // float
|
|
481
|
+
gsap.utils.random(0, 100, 5); // snapped to nearest 5
|
|
482
|
+
gsap.utils.random([10, 20, 30]); // pick from array
|
|
483
|
+
|
|
484
|
+
// snap — snap value to nearest increment or array value
|
|
485
|
+
gsap.utils.snap(10, 23); // 20
|
|
486
|
+
gsap.utils.snap([0, 50, 100], 38); // 50
|
|
487
|
+
|
|
488
|
+
// toArray — convert selector/NodeList to a real array (safe)
|
|
489
|
+
const boxes = gsap.utils.toArray(".box"); // always returns an Array
|
|
490
|
+
|
|
491
|
+
// selector — scoped querySelector factory
|
|
492
|
+
const q = gsap.utils.selector(myRef);
|
|
493
|
+
gsap.to(q(".title"), { opacity: 1 }); // only searches within myRef
|
|
494
|
+
|
|
495
|
+
// wrap — wrap index around an array
|
|
496
|
+
const colors = ["red", "green", "blue"];
|
|
497
|
+
gsap.utils.wrap(colors, 5); // "blue" (wraps around)
|
|
498
|
+
|
|
499
|
+
// pipe — compose multiple utility functions
|
|
500
|
+
const clampAndRound = gsap.utils.pipe(
|
|
501
|
+
gsap.utils.clamp(0, 100),
|
|
502
|
+
Math.round
|
|
503
|
+
);
|
|
504
|
+
clampAndRound(125.7); // 100
|
|
505
|
+
```
|
|
506
|
+
|
|
507
|
+
---
|
|
508
|
+
|
|
509
|
+
## gsap-react — React Integration
|
|
510
|
+
|
|
511
|
+
### The `useGSAP` Hook (Official)
|
|
512
|
+
|
|
513
|
+
```jsx
|
|
514
|
+
import { useGSAP } from "@gsap/react";
|
|
515
|
+
import { gsap } from "gsap";
|
|
516
|
+
|
|
517
|
+
// ❌ HALLUCINATION TRAP: Do NOT use raw useEffect for GSAP in React
|
|
518
|
+
// useEffect does not scope selectors, does not auto-cleanup, and causes
|
|
519
|
+
// double-animation bugs in React 18+ Strict Mode
|
|
520
|
+
|
|
521
|
+
function HeroSection() {
|
|
522
|
+
const containerRef = useRef(null);
|
|
523
|
+
|
|
524
|
+
useGSAP(() => {
|
|
525
|
+
// All selectors are automatically scoped to containerRef
|
|
526
|
+
gsap.from(".hero-title", { y: 60, opacity: 0, duration: 0.8 });
|
|
527
|
+
gsap.from(".hero-sub", { y: 40, opacity: 0, duration: 0.6, delay: 0.3 });
|
|
528
|
+
}, { scope: containerRef }); // scope = auto querySelector boundary
|
|
529
|
+
|
|
530
|
+
return (
|
|
531
|
+
<div ref={containerRef}>
|
|
532
|
+
<h1 className="hero-title">Welcome</h1>
|
|
533
|
+
<p className="hero-sub">Subtitle</p>
|
|
534
|
+
</div>
|
|
535
|
+
);
|
|
536
|
+
}
|
|
537
|
+
```
|
|
538
|
+
|
|
539
|
+
### Dependency Array (Re-running Animations)
|
|
540
|
+
|
|
541
|
+
```jsx
|
|
542
|
+
useGSAP(() => {
|
|
543
|
+
gsap.to(".counter", { innerText: count, snap: { innerText: 1 } });
|
|
544
|
+
}, { dependencies: [count], scope: containerRef });
|
|
545
|
+
|
|
546
|
+
// ❌ HALLUCINATION TRAP: The option is called `dependencies`, NOT `deps`
|
|
547
|
+
```
|
|
548
|
+
|
|
549
|
+
### Manual Context (Without useGSAP)
|
|
550
|
+
|
|
551
|
+
```jsx
|
|
552
|
+
useEffect(() => {
|
|
553
|
+
const ctx = gsap.context(() => {
|
|
554
|
+
gsap.from(".box", { x: -100, opacity: 0 });
|
|
555
|
+
}, containerRef); // scope
|
|
556
|
+
|
|
557
|
+
return () => ctx.revert(); // CLEANUP — kills all tweens and ScrollTriggers
|
|
558
|
+
}, []);
|
|
559
|
+
|
|
560
|
+
// gsap.context() is GSAP's own cleanup mechanism
|
|
561
|
+
// ctx.revert() kills every tween and ScrollTrigger created inside it
|
|
562
|
+
```
|
|
563
|
+
|
|
564
|
+
### Refs vs. Selectors
|
|
565
|
+
|
|
566
|
+
```jsx
|
|
567
|
+
// ✅ Preferred: useRef + scope
|
|
568
|
+
const boxRef = useRef(null);
|
|
569
|
+
useGSAP(() => {
|
|
570
|
+
gsap.to(boxRef.current, { x: 100 });
|
|
571
|
+
}, { scope: containerRef });
|
|
572
|
+
|
|
573
|
+
// ✅ Also fine: class selectors when scoped
|
|
574
|
+
useGSAP(() => {
|
|
575
|
+
gsap.to(".box", { x: 100 }); // scoped to containerRef
|
|
576
|
+
}, { scope: containerRef });
|
|
577
|
+
|
|
578
|
+
// ❌ BAD: Global selectors without scope
|
|
579
|
+
useGSAP(() => {
|
|
580
|
+
gsap.to(".box", { x: 100 }); // matches ALL .box elements globally
|
|
581
|
+
});
|
|
582
|
+
```
|
|
583
|
+
|
|
584
|
+
### SSR / Next.js Considerations
|
|
585
|
+
|
|
586
|
+
```jsx
|
|
587
|
+
// Animations must only run on the client
|
|
588
|
+
useGSAP(() => {
|
|
589
|
+
// This hook already handles client-only execution
|
|
590
|
+
gsap.from(".element", { opacity: 0 });
|
|
591
|
+
}, { scope: containerRef });
|
|
592
|
+
|
|
593
|
+
// For dynamic imports in Next.js (App Router)
|
|
594
|
+
"use client"; // ← required at top of component file
|
|
595
|
+
|
|
596
|
+
// ❌ HALLUCINATION TRAP: Do NOT import GSAP in server components
|
|
597
|
+
// GSAP requires `window` and `document` — it will crash on the server
|
|
598
|
+
```
|
|
599
|
+
|
|
600
|
+
---
|
|
601
|
+
|
|
602
|
+
## gsap-performance — Performance Optimization
|
|
603
|
+
|
|
604
|
+
### Transforms Over Layout Properties (Critical)
|
|
605
|
+
|
|
606
|
+
```js
|
|
607
|
+
// ✅ GPU-accelerated (composited — no layout recalculation)
|
|
608
|
+
gsap.to(".box", { x: 100, y: 50, rotation: 45, scale: 1.2, opacity: 0.5 });
|
|
609
|
+
|
|
610
|
+
// ❌ TRIGGERS LAYOUT REFLOW (expensive — avoid animating these)
|
|
611
|
+
gsap.to(".box", { width: 200, height: 100, top: 50, left: 100, padding: 20 });
|
|
612
|
+
|
|
613
|
+
// ❌ HALLUCINATION TRAP: AI often generates `{ left: 100 }` instead of `{ x: 100 }`
|
|
614
|
+
// x/y use CSS transform: translate(), which is GPU-composited
|
|
615
|
+
// left/top trigger layout recalculation on every frame = jank
|
|
616
|
+
```
|
|
617
|
+
|
|
618
|
+
### `will-change` Management
|
|
619
|
+
|
|
620
|
+
```js
|
|
621
|
+
// GSAP auto-applies will-change: transform during animation
|
|
622
|
+
// Do NOT manually add will-change to many elements — it wastes GPU memory
|
|
623
|
+
|
|
624
|
+
// For known long-running animations, apply manually and remove after:
|
|
625
|
+
gsap.set(".persistent-animation", { willChange: "transform" });
|
|
626
|
+
// ... animation runs ...
|
|
627
|
+
gsap.set(".persistent-animation", { willChange: "auto" }); // release GPU memory
|
|
628
|
+
```
|
|
629
|
+
|
|
630
|
+
### Batching & `gsap.ticker`
|
|
631
|
+
|
|
632
|
+
```js
|
|
633
|
+
// Use gsap.ticker instead of requestAnimationFrame for sync
|
|
634
|
+
gsap.ticker.add((time, deltaTime, frame) => {
|
|
635
|
+
// Runs in sync with GSAP's internal RAF loop
|
|
636
|
+
// Use for particle systems, canvas drawing, etc.
|
|
637
|
+
});
|
|
638
|
+
|
|
639
|
+
// Throttle ticker to 30fps (for low-power devices)
|
|
640
|
+
gsap.ticker.fps(30);
|
|
641
|
+
|
|
642
|
+
// Lazy rendering (default: true) — GSAP batches reads/writes
|
|
643
|
+
// Only disable if you need synchronous layout reads mid-animation
|
|
644
|
+
gsap.ticker.lagSmoothing(500, 33); // smooth large frame drops
|
|
645
|
+
```
|
|
646
|
+
|
|
647
|
+
### ScrollTrigger Performance
|
|
648
|
+
|
|
649
|
+
```js
|
|
650
|
+
// ❌ BAD: One ScrollTrigger per item in a long list
|
|
651
|
+
document.querySelectorAll(".item").forEach(item => {
|
|
652
|
+
ScrollTrigger.create({ trigger: item, /* ... */ }); // 100+ instances = jank
|
|
653
|
+
});
|
|
654
|
+
|
|
655
|
+
// ✅ BETTER: Use ScrollTrigger.batch()
|
|
656
|
+
ScrollTrigger.batch(".item", {
|
|
657
|
+
onEnter: (elements) => {
|
|
658
|
+
gsap.from(elements, { opacity: 0, y: 50, stagger: 0.1 });
|
|
659
|
+
},
|
|
660
|
+
onLeave: (elements) => {
|
|
661
|
+
gsap.to(elements, { opacity: 0 });
|
|
662
|
+
},
|
|
663
|
+
});
|
|
664
|
+
|
|
665
|
+
// ❌ HALLUCINATION TRAP: Never use `markers: true` in production
|
|
666
|
+
// It injects visible debug DOM elements
|
|
667
|
+
```
|
|
668
|
+
|
|
669
|
+
### Overwrite Modes
|
|
670
|
+
|
|
671
|
+
```js
|
|
672
|
+
// Prevent conflicting tweens from stacking
|
|
673
|
+
gsap.to(".box", { x: 100, overwrite: "auto" });
|
|
674
|
+
|
|
675
|
+
// Modes:
|
|
676
|
+
// "auto" — kill only conflicting properties on same target (recommended)
|
|
677
|
+
// true — kill ALL tweens on same target
|
|
678
|
+
// false — allow stacking (default, can cause jank)
|
|
679
|
+
```
|
|
680
|
+
|
|
681
|
+
---
|
|
682
|
+
|
|
683
|
+
## gsap-frameworks — Vue, Svelte & Framework Lifecycle
|
|
684
|
+
|
|
685
|
+
### Vue 3 (Composition API)
|
|
686
|
+
|
|
687
|
+
```vue
|
|
688
|
+
<script setup>
|
|
689
|
+
import { ref, onMounted, onUnmounted } from "vue";
|
|
690
|
+
import { gsap } from "gsap";
|
|
691
|
+
import { ScrollTrigger } from "gsap/ScrollTrigger";
|
|
692
|
+
|
|
693
|
+
gsap.registerPlugin(ScrollTrigger);
|
|
694
|
+
|
|
695
|
+
const containerRef = ref(null);
|
|
696
|
+
let ctx;
|
|
697
|
+
|
|
698
|
+
onMounted(() => {
|
|
699
|
+
ctx = gsap.context(() => {
|
|
700
|
+
gsap.from(".box", {
|
|
701
|
+
y: 60, opacity: 0, duration: 0.8,
|
|
702
|
+
scrollTrigger: { trigger: ".box", start: "top 80%" },
|
|
703
|
+
});
|
|
704
|
+
}, containerRef.value); // scope to component root
|
|
705
|
+
});
|
|
706
|
+
|
|
707
|
+
onUnmounted(() => {
|
|
708
|
+
ctx.revert(); // CRITICAL: kills all tweens + ScrollTriggers
|
|
709
|
+
});
|
|
710
|
+
</script>
|
|
711
|
+
|
|
712
|
+
<template>
|
|
713
|
+
<div ref="containerRef">
|
|
714
|
+
<div class="box">Animated</div>
|
|
715
|
+
</div>
|
|
716
|
+
</template>
|
|
717
|
+
```
|
|
718
|
+
|
|
719
|
+
### Svelte
|
|
720
|
+
|
|
721
|
+
```svelte
|
|
722
|
+
<script>
|
|
723
|
+
import { onMount, onDestroy } from "svelte";
|
|
724
|
+
import { gsap } from "gsap";
|
|
725
|
+
import { ScrollTrigger } from "gsap/ScrollTrigger";
|
|
726
|
+
|
|
727
|
+
gsap.registerPlugin(ScrollTrigger);
|
|
728
|
+
|
|
729
|
+
let container;
|
|
730
|
+
let ctx;
|
|
731
|
+
|
|
732
|
+
onMount(() => {
|
|
733
|
+
ctx = gsap.context(() => {
|
|
734
|
+
gsap.from(".box", { y: 60, opacity: 0, duration: 0.8 });
|
|
735
|
+
}, container);
|
|
736
|
+
});
|
|
737
|
+
|
|
738
|
+
onDestroy(() => {
|
|
739
|
+
ctx?.revert(); // cleanup on component destroy
|
|
740
|
+
});
|
|
741
|
+
</script>
|
|
742
|
+
|
|
743
|
+
<div bind:this={container}>
|
|
744
|
+
<div class="box">Animated</div>
|
|
745
|
+
</div>
|
|
746
|
+
```
|
|
747
|
+
|
|
748
|
+
### Universal Cleanup Rules
|
|
749
|
+
|
|
750
|
+
```
|
|
751
|
+
✅ ALWAYS use gsap.context() in framework components
|
|
752
|
+
✅ ALWAYS call ctx.revert() on unmount/destroy/cleanup
|
|
753
|
+
✅ ALWAYS call ScrollTrigger.kill() or .killAll() on route changes (SPA)
|
|
754
|
+
✅ ALWAYS scope selectors to the component root
|
|
755
|
+
|
|
756
|
+
❌ NEVER leave orphaned ScrollTriggers after navigation
|
|
757
|
+
❌ NEVER use global selectors without scoping in component frameworks
|
|
758
|
+
❌ NEVER forget to revert SplitText instances (corrupts DOM)
|
|
759
|
+
```
|
|
760
|
+
|
|
761
|
+
---
|
|
762
|
+
|
|
763
|
+
## Common Animation Patterns
|
|
764
|
+
|
|
765
|
+
### Fade-In On Scroll (Batch)
|
|
766
|
+
|
|
767
|
+
```js
|
|
768
|
+
ScrollTrigger.batch(".fade-in", {
|
|
769
|
+
onEnter: (elements) => {
|
|
770
|
+
gsap.fromTo(elements,
|
|
771
|
+
{ opacity: 0, y: 40 },
|
|
772
|
+
{ opacity: 1, y: 0, duration: 0.6, stagger: 0.1, ease: "power2.out" }
|
|
773
|
+
);
|
|
774
|
+
},
|
|
775
|
+
start: "top 85%",
|
|
776
|
+
});
|
|
777
|
+
```
|
|
778
|
+
|
|
779
|
+
### Horizontal Scroll Section
|
|
780
|
+
|
|
781
|
+
```js
|
|
782
|
+
const sections = gsap.utils.toArray(".panel");
|
|
783
|
+
|
|
784
|
+
gsap.to(sections, {
|
|
785
|
+
xPercent: -100 * (sections.length - 1),
|
|
786
|
+
ease: "none",
|
|
787
|
+
scrollTrigger: {
|
|
788
|
+
trigger: ".horizontal-container",
|
|
789
|
+
pin: true,
|
|
790
|
+
scrub: 1,
|
|
791
|
+
snap: 1 / (sections.length - 1),
|
|
792
|
+
end: () => "+=" + document.querySelector(".horizontal-container").offsetWidth,
|
|
793
|
+
},
|
|
794
|
+
});
|
|
795
|
+
```
|
|
796
|
+
|
|
797
|
+
### Magnetic Button
|
|
798
|
+
|
|
799
|
+
```js
|
|
800
|
+
const btn = document.querySelector(".magnetic-btn");
|
|
801
|
+
|
|
802
|
+
btn.addEventListener("mousemove", (e) => {
|
|
803
|
+
const { left, top, width, height } = btn.getBoundingClientRect();
|
|
804
|
+
const x = (e.clientX - left - width / 2) * 0.3;
|
|
805
|
+
const y = (e.clientY - top - height / 2) * 0.3;
|
|
806
|
+
gsap.to(btn, { x, y, duration: 0.3, ease: "power2.out" });
|
|
807
|
+
});
|
|
808
|
+
|
|
809
|
+
btn.addEventListener("mouseleave", () => {
|
|
810
|
+
gsap.to(btn, { x: 0, y: 0, duration: 0.5, ease: "elastic.out(1, 0.3)" });
|
|
811
|
+
});
|
|
812
|
+
```
|
|
813
|
+
|
|
814
|
+
### Counter / Number Ticker
|
|
815
|
+
|
|
816
|
+
```js
|
|
817
|
+
const obj = { val: 0 };
|
|
818
|
+
gsap.to(obj, {
|
|
819
|
+
val: 12847,
|
|
820
|
+
duration: 2,
|
|
821
|
+
ease: "power1.out",
|
|
822
|
+
snap: { val: 1 },
|
|
823
|
+
onUpdate: () => {
|
|
824
|
+
document.querySelector(".counter").textContent = obj.val.toLocaleString();
|
|
825
|
+
},
|
|
826
|
+
});
|
|
827
|
+
```
|
|
828
|
+
|
|
829
|
+
---
|
|
830
|
+
|
|
831
|
+
## Output Format
|
|
832
|
+
|
|
833
|
+
When this skill produces or reviews code, structure your output as follows:
|
|
834
|
+
|
|
835
|
+
```
|
|
836
|
+
━━━ GSAP Expert Report ━━━━━━━━━━━━━━━━━━━━━━━━
|
|
837
|
+
Skill: GSAP Expert
|
|
838
|
+
GSAP Version: 3.12+
|
|
839
|
+
Scope: [N files · N animations]
|
|
840
|
+
─────────────────────────────────────────────────
|
|
841
|
+
✅ Passed: [checks that passed, or "All clean"]
|
|
842
|
+
⚠️ Warnings: [non-blocking issues, or "None"]
|
|
843
|
+
❌ Blocked: [blocking issues requiring fix, or "None"]
|
|
844
|
+
─────────────────────────────────────────────────
|
|
845
|
+
VBC status: PENDING → VERIFIED
|
|
846
|
+
Evidence: [test output / lint pass / compile success]
|
|
847
|
+
```
|
|
848
|
+
|
|
849
|
+
**VBC (Verification-Before-Completion) is mandatory.**
|
|
850
|
+
Do not mark status as VERIFIED until concrete terminal evidence is provided.
|
|
851
|
+
|
|
852
|
+
---
|
|
853
|
+
|
|
854
|
+
## 🤖 LLM-Specific Traps
|
|
855
|
+
|
|
856
|
+
AI coding assistants often fall into specific bad habits when generating GSAP code. These are strictly forbidden:
|
|
857
|
+
|
|
858
|
+
1. **GSAP v2 Syntax:** Using `TweenMax`, `TweenLite`, `TimelineMax`, `TimelineLite`, `Power2.easeOut`, or `CSSPlugin`. These are ALL deprecated in GSAP 3+. The correct API is `gsap.to()`, `gsap.timeline()`, and `ease: "power2.out"`.
|
|
859
|
+
2. **Missing Plugin Registration:** Every GSAP plugin MUST be registered with `gsap.registerPlugin()` before use. This includes ScrollTrigger, Flip, Draggable, etc.
|
|
860
|
+
3. **Missing Cleanup:** Every `gsap.context()` must have a corresponding `.revert()` on unmount. Every `ScrollTrigger` must be `.kill()`ed. Orphaned animations cause memory leaks.
|
|
861
|
+
4. **Layout Props Instead of Transforms:** Animating `width`, `height`, `top`, `left`, `margin`, `padding` instead of `x`, `y`, `scale`, `rotation`. Layout props trigger expensive reflows.
|
|
862
|
+
5. **Using `useEffect` Instead of `useGSAP`:** In React, always prefer the official `@gsap/react` `useGSAP` hook. It handles scoping, cleanup, and React 18+ Strict Mode.
|
|
863
|
+
6. **Hallucinating Easing Names:** `easeInOut`, `Cubic.easeIn`, `easeOutBounce` do NOT exist. Correct format: `power2.inOut`, `bounce.out`, `elastic.out(1, 0.3)`.
|
|
864
|
+
7. **Confusing `duration` Units:** GSAP uses seconds. CSS uses milliseconds. `duration: 300` in GSAP means 5 minutes, not 300ms.
|
|
865
|
+
8. **Shipping `markers: true`:** Debug markers must never reach production.
|
|
866
|
+
9. **Hallucinated Imports:** Using `import gsap from "gsap"` (default import) instead of `import { gsap } from "gsap"` (named import). Both work, but named is the documented standard.
|
|
867
|
+
10. **Inventing Plugin Names:** Only use real GSAP plugins. There is no `ParallaxPlugin`, `FadePlugin`, or `AnimatePlugin`.
|
|
868
|
+
|
|
869
|
+
---
|
|
870
|
+
|
|
871
|
+
## 🏛️ Tribunal Integration (Anti-Hallucination)
|
|
872
|
+
|
|
873
|
+
**Slash command: `/review` or `/tribunal-full`**
|
|
874
|
+
**Active reviewers: `logic-reviewer` · `security-auditor` · `performance-optimizer`**
|
|
875
|
+
|
|
876
|
+
### ❌ Forbidden AI Tropes
|
|
877
|
+
|
|
878
|
+
1. **Blind Assumptions:** Never make an assumption without documenting it clearly with `// VERIFY: [reason]`.
|
|
879
|
+
2. **Silent Degradation:** Catching and suppressing GSAP errors without logging or handling.
|
|
880
|
+
3. **Context Amnesia:** Forgetting the user's constraints (e.g., generating Club plugin code when the user has a free license).
|
|
881
|
+
4. **Over-Animating:** Adding animations to every element "because it looks cool." Every animation must serve a UX purpose.
|
|
882
|
+
|
|
883
|
+
### ✅ Pre-Flight Self-Audit
|
|
884
|
+
|
|
885
|
+
Review these questions before confirming output:
|
|
886
|
+
```
|
|
887
|
+
✅ Did I use GSAP 3+ syntax exclusively (no TweenMax/TweenLite)?
|
|
888
|
+
✅ Did I register every plugin with gsap.registerPlugin()?
|
|
889
|
+
✅ Did I clean up with gsap.context().revert() or ScrollTrigger.kill()?
|
|
890
|
+
✅ Did I use transforms (x, y, scale, rotation) instead of layout props?
|
|
891
|
+
✅ Did I use useGSAP (not useEffect) for React components?
|
|
892
|
+
✅ Did I mark Club-only plugins with a license warning comment?
|
|
893
|
+
✅ Did I remove markers: true from production code?
|
|
894
|
+
✅ Did I respect prefers-reduced-motion for accessibility?
|
|
895
|
+
```
|
|
896
|
+
|
|
897
|
+
### 🛑 Verification-Before-Completion (VBC) Protocol
|
|
898
|
+
|
|
899
|
+
**CRITICAL:** You must follow a strict "evidence-based closeout" state machine.
|
|
900
|
+
- ❌ **Forbidden:** Declaring a task complete because the output "looks correct."
|
|
901
|
+
- ✅ **Required:** You are explicitly forbidden from finalizing any task without providing **concrete evidence** (terminal output, passing tests, compile success, or equivalent proof) that your output works as intended.
|