akili-specs 2.7.1 → 2.9.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.
Files changed (70) hide show
  1. package/.claude/commands/akili-archive.md +2 -0
  2. package/.claude/commands/akili-audit.md +4 -0
  3. package/.claude/commands/akili-constitution.md +104 -14
  4. package/.claude/commands/akili-execute.md +5 -3
  5. package/.claude/commands/akili-propose.md +2 -0
  6. package/.claude/commands/akili-quick.md +2 -0
  7. package/.claude/commands/akili-seo.md +2 -0
  8. package/.claude/commands/akili-specify.md +7 -2
  9. package/.claude/commands/akili-test.md +4 -2
  10. package/.claude/commands/akili-validate.md +2 -0
  11. package/.claude/skills/angular-developer/SKILL.md +4 -0
  12. package/.claude/skills/api-design-principles/SKILL.md +8 -0
  13. package/.claude/skills/aws-serverless/SKILL.md +8 -1
  14. package/.claude/skills/brainstorming/SKILL.md +25 -1
  15. package/.claude/skills/cognitive-doc-design/SKILL.md +14 -0
  16. package/.claude/skills/error-handling-patterns/SKILL.md +8 -0
  17. package/.claude/skills/frontend-design/SKILL.md +8 -1
  18. package/.claude/skills/gsap-animation/SKILL.md +182 -0
  19. package/.claude/skills/gsap-animation/references/frameworks.md +137 -0
  20. package/.claude/skills/{gsap-performance/SKILL.md → gsap-animation/references/performance.md} +10 -20
  21. package/.claude/skills/gsap-animation/references/plugins.md +390 -0
  22. package/.claude/skills/gsap-animation/references/react.md +122 -0
  23. package/.claude/skills/gsap-animation/references/scrolltrigger.md +255 -0
  24. package/.claude/skills/{gsap-timeline/SKILL.md → gsap-animation/references/timeline.md} +7 -19
  25. package/.claude/skills/gsap-animation/references/utils.md +254 -0
  26. package/.claude/skills/judgment-day/SKILL.md +19 -1
  27. package/.claude/skills/kaizen/SKILL.md +1 -0
  28. package/.claude/skills/nestjs-expert/SKILL.md +8 -0
  29. package/.claude/skills/product-manager-toolkit/SKILL.md +7 -1
  30. package/.claude/skills/react-doctor/SKILL.md +6 -0
  31. package/.claude/skills/seo-audit/SKILL.md +18 -9
  32. package/.claude/skills/shadcn-ui/SKILL.md +6 -0
  33. package/.claude/skills/stitch-design/SKILL.md +7 -0
  34. package/.claude/skills/systematic-debugging/SKILL.md +21 -0
  35. package/.claude/skills/tailwind-design-system/SKILL.md +8 -0
  36. package/.claude/skills/ui-ux-pro-max/SKILL.md +21 -0
  37. package/.claude/skills/vercel-react-best-practices/SKILL.md +3 -0
  38. package/.claude/templates/leader.md +1 -1
  39. package/CHANGELOG.md +31 -0
  40. package/README.md +3 -8
  41. package/bin/akili.js +40 -0
  42. package/docs/cli.md +1 -1
  43. package/docs/commands/akili-constitution.md +17 -13
  44. package/docs/flow.md +39 -5
  45. package/docs/model-routing.md +92 -24
  46. package/docs/skills/README.md +34 -31
  47. package/docs/skills/brainstorming.md +2 -0
  48. package/docs/skills/cognitive-doc-design.md +2 -0
  49. package/docs/skills/governance.md +77 -0
  50. package/docs/skills/gsap-animation.md +29 -0
  51. package/docs/skills/judgment-day.md +2 -0
  52. package/docs/skills/product-manager-toolkit.md +1 -1
  53. package/docs/skills/seo-audit.md +2 -0
  54. package/docs/skills/systematic-debugging.md +2 -0
  55. package/docs/skills/ui-ux-pro-max.md +2 -0
  56. package/package.json +1 -1
  57. package/.claude/skills/gsap-core/SKILL.md +0 -254
  58. package/.claude/skills/gsap-frameworks/SKILL.md +0 -153
  59. package/.claude/skills/gsap-plugins/SKILL.md +0 -426
  60. package/.claude/skills/gsap-react/SKILL.md +0 -136
  61. package/.claude/skills/gsap-scrolltrigger/SKILL.md +0 -296
  62. package/.claude/skills/gsap-utils/SKILL.md +0 -284
  63. package/docs/skills/gsap-core.md +0 -21
  64. package/docs/skills/gsap-frameworks.md +0 -20
  65. package/docs/skills/gsap-performance.md +0 -21
  66. package/docs/skills/gsap-plugins.md +0 -20
  67. package/docs/skills/gsap-react.md +0 -22
  68. package/docs/skills/gsap-scrolltrigger.md +0 -21
  69. package/docs/skills/gsap-timeline.md +0 -20
  70. package/docs/skills/gsap-utils.md +0 -21
@@ -0,0 +1,390 @@
1
+ # GSAP Plugins
2
+
3
+ Read when using any GSAP plugin: scroll-to, ScrollSmoother, Flip/FLIP, Draggable, Inertia, Observer, SplitText, ScrambleText, DrawSVG, MorphSVG, MotionPath, physics, easing plugins (CustomEase, EasePack, CustomWiggle, CustomBounce), GSDevTools, or Pixi. (ScrollTrigger has its own file: `references/scrolltrigger.md`.)
4
+
5
+ ## Registering Plugins
6
+
7
+ Register each plugin once before first use (see SKILL.md for the shared pattern):
8
+
9
+ ```javascript
10
+ import gsap from "gsap";
11
+ import { ScrollToPlugin } from "gsap/ScrollToPlugin";
12
+ import { Flip } from "gsap/Flip";
13
+ import { Draggable } from "gsap/Draggable";
14
+
15
+ gsap.registerPlugin(ScrollToPlugin, Flip, Draggable);
16
+ ```
17
+
18
+ - ✅ Register before using the plugin in any tween or API call.
19
+ - ✅ In React, register at top level or once in the app (before first `useGSAP`); do not register inside a component that re-renders. `useGSAP` is itself a plugin that needs registration.
20
+
21
+ ## Scroll
22
+
23
+ ### ScrollToPlugin
24
+
25
+ Animates scroll position (window or a scrollable element). Use for "scroll to element/position" without ScrollTrigger.
26
+
27
+ ```javascript
28
+ gsap.registerPlugin(ScrollToPlugin);
29
+
30
+ gsap.to(window, { duration: 1, scrollTo: { y: 500 } });
31
+ gsap.to(window, { duration: 1, scrollTo: { y: "#section", offsetY: 50 } });
32
+ gsap.to(scrollContainer, { duration: 1, scrollTo: { x: "max" } });
33
+ ```
34
+
35
+ | Option | Description |
36
+ |--------|-------------|
37
+ | `x`, `y` | Target scroll position (number), or `"max"` for maximum |
38
+ | `element` | Selector or element to scroll to (scroll-into-view) |
39
+ | `offsetX`, `offsetY` | Offset in pixels from the target position |
40
+
41
+ ### ScrollSmoother
42
+
43
+ Smooth scroll wrapper (smooths native scroll). Requires ScrollTrigger and a specific DOM structure. Register after ScrollTrigger.
44
+
45
+ ```html
46
+ <body>
47
+ <div id="smooth-wrapper">
48
+ <div id="smooth-content">
49
+ <!-- ALL YOUR CONTENT HERE -->
50
+ </div>
51
+ </div>
52
+ <!-- position: fixed elements can go outside -->
53
+ </body>
54
+ ```
55
+
56
+ ## DOM / UI
57
+
58
+ ### Flip
59
+
60
+ Capture state with `Flip.getState()`, change the DOM (layout/classes), then `Flip.from()` animates from the previous state to the new state (FLIP: First, Last, Invert, Play). Use when animating between two layout states (lists, grids, expand/collapse).
61
+
62
+ ```javascript
63
+ gsap.registerPlugin(Flip);
64
+
65
+ const state = Flip.getState(".item");
66
+ // change DOM (reorder, add/remove, change classes)
67
+ Flip.from(state, { duration: 0.5, ease: "power2.inOut" });
68
+ ```
69
+
70
+ | Option | Description |
71
+ |--------|-------------|
72
+ | `absolute` | Use `position: absolute` during the flip (default `false`) |
73
+ | `nested` | When true, only the first level of children is measured (better for nested transforms) |
74
+ | `scale` | When true, scale elements to fit (avoids stretch); default `true` |
75
+ | `simple` | When true, only position/scale animated (faster, less accurate) |
76
+ | `duration`, `ease` | Standard tween options |
77
+
78
+ Docs: https://gsap.com/docs/v3/Plugins/Flip
79
+
80
+ ### Draggable
81
+
82
+ Makes elements draggable, spinnable, or throwable. Use for sliders, cards, reorderable lists, or any drag interaction.
83
+
84
+ ```javascript
85
+ gsap.registerPlugin(Draggable, InertiaPlugin);
86
+
87
+ Draggable.create(".box", { type: "x,y", bounds: "#container", inertia: true });
88
+ Draggable.create(".knob", { type: "rotation" });
89
+ ```
90
+
91
+ | Option | Description |
92
+ |--------|-------------|
93
+ | `type` | `"x"`, `"y"`, `"x,y"`, `"rotation"`, `"scroll"` |
94
+ | `bounds` | Element, selector, or `{ minX, maxX, minY, maxY }` to constrain drag |
95
+ | `inertia` | `true` for throw/momentum (requires InertiaPlugin) |
96
+ | `edgeResistance` | 0–1; resistance when dragging past bounds |
97
+ | `cursor` | CSS cursor during drag |
98
+ | `onDragStart`, `onDrag`, `onDragEnd` | Callbacks; receive event and target |
99
+ | `onThrowUpdate`, `onThrowComplete` | Callbacks when inertia is active |
100
+
101
+ ### Inertia (InertiaPlugin)
102
+
103
+ Works with Draggable for momentum after release, or tracks the velocity of any property so it can glide to a stop. Register with Draggable when using `inertia: true`:
104
+
105
+ ```javascript
106
+ gsap.registerPlugin(Draggable, InertiaPlugin);
107
+ Draggable.create(".box", { type: "x,y", inertia: true });
108
+ ```
109
+
110
+ Track velocity of a property, then use `"auto"` to continue current velocity and glide to a stop:
111
+
112
+ ```javascript
113
+ InertiaPlugin.track(".box", "x");
114
+ gsap.to(obj, { inertia: { x: "auto" } });
115
+ ```
116
+
117
+ ### Observer
118
+
119
+ Normalizes pointer and scroll input across devices. Use for swipe, scroll direction, or custom gesture logic without tying to scroll position.
120
+
121
+ ```javascript
122
+ gsap.registerPlugin(Observer);
123
+
124
+ Observer.create({
125
+ target: "#area",
126
+ onUp: () => {},
127
+ onDown: () => {},
128
+ onLeft: () => {},
129
+ onRight: () => {},
130
+ tolerance: 10
131
+ });
132
+ ```
133
+
134
+ | Option | Description |
135
+ |--------|-------------|
136
+ | `target` | Element or selector to observe |
137
+ | `onUp`, `onDown`, `onLeft`, `onRight` | Callbacks when swipe/scroll passes tolerance in that direction |
138
+ | `tolerance` | Pixels before direction is detected; default 10 |
139
+ | `type` | `"touch"`, `"pointer"`, or `"wheel"` (default `"touch,pointer"`) |
140
+
141
+ ## Text
142
+
143
+ ### SplitText
144
+
145
+ Splits an element's text into characters, words, and/or lines (each in its own element) for staggered animation. Returns an instance with **chars**, **words**, **lines** (and **masks** when `mask` is set). Restore original markup with **revert()** or let **gsap.context()** revert. Integrates with **gsap.context()**, **matchMedia()**, and **useGSAP()**. API: **SplitText.create(target, vars)**.
146
+
147
+ ```javascript
148
+ gsap.registerPlugin(SplitText);
149
+
150
+ const split = SplitText.create(".heading", { type: "words, chars" });
151
+ gsap.from(split.chars, { opacity: 0, y: 20, stagger: 0.03, duration: 0.4 });
152
+ // later: split.revert() or let gsap.context() cleanup revert
153
+ ```
154
+
155
+ With **onSplit()** (3.13.0+), animations run on each split and on re-split when **autoSplit** is used; returning a tween/timeline from **onSplit()** lets SplitText clean up and sync progress on re-split:
156
+
157
+ ```javascript
158
+ SplitText.create(".split", {
159
+ type: "lines",
160
+ autoSplit: true,
161
+ onSplit(self) {
162
+ return gsap.from(self.lines, { y: 100, opacity: 0, stagger: 0.05, duration: 0.5 });
163
+ }
164
+ });
165
+ ```
166
+
167
+ | Option | Description |
168
+ |--------|-------------|
169
+ | **type** | Comma-separated: `"chars"`, `"words"`, `"lines"`. Default `"chars,words,lines"`. Split only what's needed for performance. Avoid chars-only without words/lines, or use **smartWrap: true**. |
170
+ | **charsClass**, **wordsClass**, **linesClass** | CSS class on each split element. Append `"++"` for an incremented class (e.g. `linesClass: "line++"` → `line1`, `line2`, …). |
171
+ | **aria** | `"auto"` (default), `"hidden"`, or `"none"`. `"auto"` adds `aria-label` on the element and `aria-hidden` on split units; `"hidden"` hides all from readers; `"none"` leaves aria unchanged. |
172
+ | **autoSplit** | When `true`, reverts and re-splits when fonts load or element width changes (lines split), avoiding wrong line breaks. **Animations must be created inside onSplit()**; **return** the animation for automatic cleanup and time-sync on re-split. |
173
+ | **onSplit(self)** | Callback when split completes (and on each re-split if **autoSplit**). Returning a tween/timeline enables automatic revert/sync on re-split. |
174
+ | **mask** | `"lines"`, `"words"`, or `"chars"`. Wraps each unit with `overflow: clip` for mask/reveal effects. Access wrappers on the instance's **masks** array. |
175
+ | **tag** | Wrapper tag; default `"div"`. Use `"span"` for inline (transforms like rotation/scale may not render on inline elements in some browsers). |
176
+ | **deepSlice** | When `true` (default), nested elements spanning multiple lines are subdivided so lines don't stretch vertically. Line-splitting only. |
177
+ | **ignore** | Selector or element(s) to leave unsplit (e.g. `ignore: "sup"`). |
178
+ | **smartWrap** | When splitting **chars** only, wraps words in `white-space: nowrap` to avoid mid-word breaks. Ignored if words/lines are split. Default `false`. |
179
+ | **wordDelimiter** | Word boundary: string (default `" "`), RegExp, or `{ delimiter: RegExp, replaceWith: string }`. |
180
+ | **prepareText(text, parent)** | Function returning modified text before splitting (e.g. insert break markers for languages without spaces). |
181
+ | **propIndex** | When `true`, adds a CSS variable with index on each split element (`--word: 1`, `--char: 2`). |
182
+ | **reduceWhiteSpace** | Collapse consecutive spaces; default `true`. From 3.13.0 also honors line breaks / `<pre>`. |
183
+ | **onRevert** | Callback when the instance is reverted. |
184
+
185
+ **Tips:** Split only what is animated. For custom fonts, split after they load (`document.fonts.ready.then(...)`) or use **autoSplit: true** with **onSplit()**. To avoid kerning shift when splitting chars, use CSS `font-kerning: none; text-rendering: optimizeSpeed;`. Avoid `text-wrap: balance`. SplitText does not support SVG `<text>`.
186
+
187
+ Docs: [SplitText](https://gsap.com/docs/v3/Plugins/SplitText/)
188
+
189
+ ### ScrambleText
190
+
191
+ Animates text with a scramble/glitch effect.
192
+
193
+ ```javascript
194
+ gsap.registerPlugin(ScrambleTextPlugin);
195
+
196
+ gsap.to(".text", {
197
+ duration: 1,
198
+ scrambleText: { text: "New message", chars: "01", revealDelay: 0.5 }
199
+ });
200
+ ```
201
+
202
+ ## SVG
203
+
204
+ ### DrawSVG (DrawSVGPlugin)
205
+
206
+ Reveals/hides the stroke of SVG elements by animating `stroke-dashoffset` / `stroke-dasharray`. Works on `<path>`, `<line>`, `<polyline>`, `<polygon>`, `<rect>`, `<ellipse>`. Use for "drawing"/"erasing" strokes.
207
+
208
+ **drawSVG value:** Describes the **visible segment** of the stroke (`"start end"` in percent or length), not "A to B over time." `"0% 100%"` = full stroke; `"20% 80%"` = stroke only between 20% and 80%. The tween animates from the element's **current** segment to the **target** segment. Single value (e.g. `0`, `"100%"`) means start is 0.
209
+
210
+ **Required:** The element must have a visible stroke (`stroke` + `stroke-width`), else nothing draws.
211
+
212
+ ```javascript
213
+ gsap.registerPlugin(DrawSVGPlugin);
214
+
215
+ gsap.from("#path", { duration: 1, drawSVG: 0 }); // nothing → full stroke
216
+ gsap.fromTo("#path", { drawSVG: "0% 0%" }, { drawSVG: "0% 100%", duration: 1 });
217
+ gsap.to("#path", { duration: 1, drawSVG: "20% 80%" }); // middle only
218
+ ```
219
+
220
+ **Caveats:** Only affects stroke (not fill). Prefer single-segment `<path>` elements. Contents of `<use>` cannot be visually changed. **DrawSVGPlugin.getLength(element)** and **DrawSVGPlugin.getPosition(element)** return stroke length/position.
221
+
222
+ Docs: [DrawSVG](https://gsap.com/docs/v3/Plugins/DrawSVGPlugin)
223
+
224
+ ### MorphSVG (MorphSVGPlugin)
225
+
226
+ Morphs one SVG shape into another by animating the `d` attribute. Start/end shapes need not share point counts. Works on `<path>`, `<polyline>`, `<polygon>`; primitives are converted internally or via **MorphSVGPlugin.convertToPath()**.
227
+
228
+ **morphSVG value:** a **selector**, **element**, **raw path data**, or (for polygon/polyline) a **points string**. Full config uses the **object form** with **shape** required.
229
+
230
+ ```javascript
231
+ gsap.registerPlugin(MorphSVGPlugin);
232
+
233
+ MorphSVGPlugin.convertToPath("circle, rect, ellipse, line"); // if needed
234
+
235
+ gsap.to("#diamond", { duration: 1, morphSVG: "#lightning", ease: "power2.inOut" });
236
+ gsap.to("#diamond", {
237
+ duration: 1,
238
+ morphSVG: { shape: "#lightning", type: "rotational", shapeIndex: 2 }
239
+ });
240
+ ```
241
+
242
+ | Option | Description |
243
+ |--------|-------------|
244
+ | **shape** | _(Required.)_ Target shape: selector, element, or raw path string. |
245
+ | **type** | `"linear"` (default) or `"rotational"`. Rotational uses angle/length interpolation and can avoid mid-morph kinks. |
246
+ | **map** | Segment matching: `"size"` (default), `"position"`, or `"complexity"`. |
247
+ | **shapeIndex** | Offsets which start point maps to the first end point (avoids crossing/inverting). Number for single-segment; **array** for multi-segment (negative reverses that segment). Use **shapeIndex: "log"** once to log the auto value; **findShapeIndex(start, end)** gives an interactive UI. Closed paths only. |
248
+ | **smooth** | (3.14+). Smoothing points. Number, `"auto"`, or object `{ points, redraw, persist }`. `redraw: false` keeps original anchors; `persist: false` removes added points on end. |
249
+ | **curveMode** | Boolean (3.14+). Interpolates control-handle angle/length instead of raw x/y to avoid kinks. |
250
+ | **origin** | Rotation origin for **type: "rotational"**. `"50% 50%"` (default) or `"20% 60%, 35% 90%"` for different start/end origins. |
251
+ | **precision** | Decimal places for output path data; default `2`. |
252
+ | **precompile** | Array of precomputed path strings (or `"log"` once, copy from console). Skips startup calc for complex morphs; `<path>` only. |
253
+ | **render** | Function(rawPath, target) each update — e.g. draw to canvas. |
254
+ | **updateTarget** | Set `false` (with **render**, e.g. canvas-only) so the original `<path>` isn't updated. |
255
+
256
+ **Utilities:** **convertToPath(selector | element)**, **rawPathToString(rawPath)**, **stringToRawPath(d)**. The plugin stores the original `d` for tweening back.
257
+
258
+ **Tips:** For twisted/inverted morphs set **shapeIndex** (`"log"` or findShapeIndex()). Precompile only when the first frame is slow; it doesn't fix jank during the tween.
259
+
260
+ Docs: [MorphSVG](https://gsap.com/docs/v3/Plugins/MorphSVGPlugin)
261
+
262
+ ### MotionPath (MotionPathPlugin)
263
+
264
+ Animates an element along an SVG path.
265
+
266
+ ```javascript
267
+ gsap.registerPlugin(MotionPathPlugin);
268
+
269
+ gsap.to(".dot", {
270
+ duration: 2,
271
+ motionPath: { path: "#path", align: "#path", alignOrigin: [0.5, 0.5] }
272
+ });
273
+ ```
274
+
275
+ | Option | Description |
276
+ |--------|-------------|
277
+ | `path` | SVG path element, selector, or path data string |
278
+ | `align` | Path element or selector to align the target to |
279
+ | `alignOrigin` | `[x, y]` origin (0–1); default `[0.5, 0.5]` |
280
+ | `autoRotate` | Rotate element to follow path tangent |
281
+ | `curviness` | 0–2; path smoothing |
282
+
283
+ ### MotionPathHelper
284
+
285
+ Visual editor for MotionPath (alignment, offset). Development only.
286
+
287
+ ```javascript
288
+ gsap.registerPlugin(MotionPathPlugin, MotionPathHelperPlugin);
289
+
290
+ const helper = MotionPathHelper.create(".dot", "#path", { end: 0.5 });
291
+ ```
292
+
293
+ ## Easing
294
+
295
+ ### CustomEase
296
+
297
+ Custom easing curves (cubic-bezier or SVG path) — use when a built-in ease is not enough. Register when using:
298
+
299
+ ```javascript
300
+ gsap.registerPlugin(CustomEase);
301
+
302
+ // Simple cubic-bezier (same values as CSS cubic-bezier())
303
+ const myEase = CustomEase.create("my-ease", ".17,.67,.83,.67");
304
+ gsap.to(".item", { x: 100, ease: myEase, duration: 1 });
305
+
306
+ // Complex curve with any number of control points, as normalized SVG path data
307
+ const hop = CustomEase.create("hop", "M0,0 C0,0 0.056,0.442 0.175,0.442 0.294,0.442 0.332,0 0.332,0 0.332,0 0.414,1 0.671,1 0.991,1 1,0 1,0");
308
+ gsap.to(".item", { x: 100, ease: hop, duration: 1 });
309
+ ```
310
+
311
+ ### EasePack
312
+
313
+ Adds more named eases (SlowMo, RoughEase, ExpoScaleEase). Register and use by name in tweens.
314
+
315
+ ### CustomWiggle
316
+
317
+ Wiggle/shake easing — a value oscillates multiple times.
318
+
319
+ ### CustomBounce
320
+
321
+ Bounce-style easing with configurable strength.
322
+
323
+ ## Physics
324
+
325
+ ### Physics2D (Physics2DPlugin)
326
+
327
+ 2D physics (velocity, angle, gravity). Use for projectiles, bouncing.
328
+
329
+ ```javascript
330
+ gsap.registerPlugin(Physics2DPlugin);
331
+
332
+ gsap.to(".ball", {
333
+ duration: 2,
334
+ physics2D: { velocity: 250, angle: 80, gravity: 500 }
335
+ });
336
+ ```
337
+
338
+ ### PhysicsProps (PhysicsPropsPlugin)
339
+
340
+ Applies physics to property values.
341
+
342
+ ```javascript
343
+ gsap.registerPlugin(PhysicsPropsPlugin);
344
+
345
+ gsap.to(".obj", {
346
+ duration: 2,
347
+ physicsProps: {
348
+ x: { velocity: 100, end: 300 },
349
+ y: { velocity: -50, acceleration: 200 }
350
+ }
351
+ });
352
+ ```
353
+
354
+ ## Development
355
+
356
+ ### GSDevTools
357
+
358
+ UI for scrubbing timelines, toggling, and debugging. **Development only — do not ship.**
359
+
360
+ ```javascript
361
+ gsap.registerPlugin(GSDevTools);
362
+ GSDevTools.create({ animation: tl });
363
+ ```
364
+
365
+ ## Other
366
+
367
+ ### Pixi (PixiPlugin)
368
+
369
+ Integrates GSAP with PixiJS for animating Pixi display objects.
370
+
371
+ ```javascript
372
+ gsap.registerPlugin(PixiPlugin);
373
+
374
+ const sprite = new PIXI.Sprite(texture);
375
+ gsap.to(sprite, { pixi: { x: 200, y: 100, scale: 1.5 }, duration: 1 });
376
+ ```
377
+
378
+ ## Best practices
379
+
380
+ - ✅ Register every plugin used with **gsap.registerPlugin()** before first use.
381
+ - ✅ Use **Flip.getState()** → DOM change → **Flip.from()** for layout transitions; use **Draggable** + **InertiaPlugin** for drag with momentum.
382
+ - ✅ Revert plugin instances (e.g. `splitInstance.revert()`) when components unmount or elements are removed.
383
+
384
+ ## Do Not
385
+
386
+ - ❌ Use a plugin in a tween or API without registering it first.
387
+ - ❌ Ship GSDevTools or development-only plugins to production.
388
+
389
+ ### Learn More
390
+ https://gsap.com/docs/v3/Plugins/
@@ -0,0 +1,122 @@
1
+ # GSAP with React
2
+
3
+ Read when writing or reviewing GSAP in React or Next.js: setup, cleanup on unmount, scoping, context-safe callbacks, and SSR. For Vue/Svelte see `references/frameworks.md`.
4
+
5
+ ## Installation
6
+
7
+ ```bash
8
+ npm install gsap
9
+ npm install @gsap/react
10
+ ```
11
+
12
+ ## Prefer the useGSAP() Hook
13
+
14
+ When **@gsap/react** is available, use **useGSAP()** instead of `useEffect()` for GSAP setup. It handles cleanup automatically and provides a scope and **contextSafe** for callbacks.
15
+
16
+ ```javascript
17
+ import { useGSAP } from "@gsap/react";
18
+
19
+ gsap.registerPlugin(useGSAP); // register before running useGSAP or any GSAP code
20
+
21
+ const containerRef = useRef(null);
22
+
23
+ useGSAP(() => {
24
+ gsap.to(".box", { x: 100 });
25
+ gsap.from(".item", { opacity: 0, stagger: 0.1 });
26
+ }, { scope: containerRef });
27
+ ```
28
+
29
+ - ✅ Pass a **scope** (ref or element) so selectors like `.box` are scoped to that root.
30
+ - ✅ Cleanup (reverting animations and ScrollTriggers) runs automatically on unmount.
31
+ - ✅ Use **contextSafe** from the hook's return value to wrap callbacks (e.g. onComplete) so they no-op after unmount and avoid React warnings.
32
+
33
+ ## Refs for Targets
34
+
35
+ Use **refs** so GSAP targets the actual DOM nodes after render. Do not rely on selector strings that might match multiple or wrong elements across re-renders unless a `scope` is defined. With useGSAP, pass the ref as **scope**; with useEffect, pass it as the second argument to `gsap.context()`. For multiple elements, ref the container and query children, or use an array of refs.
36
+
37
+ ## Dependency array, scope, and revertOnUpdate
38
+
39
+ By default, useGSAP() passes an empty dependency array to its internal effect so it doesn't run on every render. The 2nd argument is optional — a dependency array (like useEffect) or a config object:
40
+
41
+ ```javascript
42
+ useGSAP(() => {
43
+ // gsap code here, just like in a useEffect()
44
+ }, {
45
+ dependencies: [endX], // dependency array (optional)
46
+ scope: container, // scope selector text (optional, recommended)
47
+ revertOnUpdate: true // revert the context and run cleanup every time the hook re-synchronizes (when any dependency changes)
48
+ });
49
+ ```
50
+
51
+ ## gsap.context() in useEffect (when useGSAP isn't used)
52
+
53
+ Use **gsap.context()** inside a regular **useEffect()** when @gsap/react is not available or you need the effect's dependency/trigger behavior. **Always** call **ctx.revert()** in the cleanup so animations and ScrollTriggers are killed and inline styles reverted — otherwise you leak and update detached nodes.
54
+
55
+ ```javascript
56
+ useEffect(() => {
57
+ const ctx = gsap.context(() => {
58
+ gsap.to(".box", { x: 100 });
59
+ gsap.from(".item", { opacity: 0, stagger: 0.1 });
60
+ }, containerRef);
61
+ return () => ctx.revert();
62
+ }, []);
63
+ ```
64
+
65
+ - ✅ Pass a **scope** (ref or element) as the second argument so selectors are scoped to that node.
66
+ - ✅ **Always** return a cleanup that calls **ctx.revert()**.
67
+
68
+ ## Context-Safe Callbacks
69
+
70
+ GSAP objects created inside functions that run AFTER useGSAP executes (e.g. pointer event handlers) are not in the context, so they won't be reverted on unmount/re-render. Wrap those functions in **contextSafe** (from useGSAP):
71
+
72
+ ```javascript
73
+ const container = useRef();
74
+ const badRef = useRef();
75
+ const goodRef = useRef();
76
+
77
+ useGSAP((context, contextSafe) => {
78
+ // ✅ safe, created during execution
79
+ gsap.to(goodRef.current, { x: 100 });
80
+
81
+ // ❌ DANGER! Created in an event handler that runs AFTER useGSAP(). Not added to
82
+ // the context, so it won't be reverted. The listener isn't removed either, so it
83
+ // persists between renders (bad).
84
+ badRef.current.addEventListener('click', () => {
85
+ gsap.to(badRef.current, { y: 100 });
86
+ });
87
+
88
+ // ✅ safe, wrapped in contextSafe()
89
+ const onClickGood = contextSafe(() => {
90
+ gsap.to(goodRef.current, { rotation: 180 });
91
+ });
92
+ goodRef.current.addEventListener('click', onClickGood);
93
+
94
+ // 👍 remove the listener in cleanup
95
+ return () => {
96
+ goodRef.current.removeEventListener('click', onClickGood);
97
+ };
98
+ }, { scope: container });
99
+ ```
100
+
101
+ ## Server-Side Rendering (Next.js, etc.)
102
+
103
+ GSAP runs in the browser. Do not call gsap or ScrollTrigger during SSR.
104
+
105
+ - Use **useGSAP** (or useEffect) so all GSAP code runs only on the client.
106
+ - If GSAP is imported at top level, ensure the app doesn't execute `gsap.*` or `ScrollTrigger.*` during server render. Dynamic import inside useEffect is an option if bundle size/tree-shaking matters.
107
+
108
+ ## Best practices
109
+
110
+ - ✅ Prefer **useGSAP()** over `useEffect`/`useLayoutEffect`; use **gsap.context()** + **ctx.revert()** in `useEffect` when useGSAP isn't an option.
111
+ - ✅ Use refs for targets and pass a **scope** so selectors are limited to the component.
112
+ - ✅ Run GSAP only on the client; never call gsap or ScrollTrigger during SSR.
113
+
114
+ ## Do Not
115
+
116
+ - ❌ Target by **selector without a scope**; always pass **scope** (ref or element) so selectors like `.box` are limited to that root.
117
+ - ❌ Animate using selector strings that can match elements outside the component unless a `scope` is defined.
118
+ - ❌ Skip cleanup; always revert context or kill tweens/ScrollTriggers in the effect return.
119
+ - ❌ Run GSAP or ScrollTrigger during SSR; keep all usage inside client-only lifecycle.
120
+
121
+ ### Learn More
122
+ https://gsap.com/resources/React