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.
- package/.claude/commands/akili-archive.md +2 -0
- package/.claude/commands/akili-audit.md +4 -0
- package/.claude/commands/akili-constitution.md +104 -14
- package/.claude/commands/akili-execute.md +5 -3
- package/.claude/commands/akili-propose.md +2 -0
- package/.claude/commands/akili-quick.md +2 -0
- package/.claude/commands/akili-seo.md +2 -0
- package/.claude/commands/akili-specify.md +7 -2
- package/.claude/commands/akili-test.md +4 -2
- package/.claude/commands/akili-validate.md +2 -0
- package/.claude/skills/angular-developer/SKILL.md +4 -0
- package/.claude/skills/api-design-principles/SKILL.md +8 -0
- package/.claude/skills/aws-serverless/SKILL.md +8 -1
- package/.claude/skills/brainstorming/SKILL.md +25 -1
- package/.claude/skills/cognitive-doc-design/SKILL.md +14 -0
- package/.claude/skills/error-handling-patterns/SKILL.md +8 -0
- package/.claude/skills/frontend-design/SKILL.md +8 -1
- package/.claude/skills/gsap-animation/SKILL.md +182 -0
- package/.claude/skills/gsap-animation/references/frameworks.md +137 -0
- package/.claude/skills/{gsap-performance/SKILL.md → gsap-animation/references/performance.md} +10 -20
- package/.claude/skills/gsap-animation/references/plugins.md +390 -0
- package/.claude/skills/gsap-animation/references/react.md +122 -0
- package/.claude/skills/gsap-animation/references/scrolltrigger.md +255 -0
- package/.claude/skills/{gsap-timeline/SKILL.md → gsap-animation/references/timeline.md} +7 -19
- package/.claude/skills/gsap-animation/references/utils.md +254 -0
- package/.claude/skills/judgment-day/SKILL.md +19 -1
- package/.claude/skills/kaizen/SKILL.md +1 -0
- package/.claude/skills/nestjs-expert/SKILL.md +8 -0
- package/.claude/skills/product-manager-toolkit/SKILL.md +7 -1
- package/.claude/skills/react-doctor/SKILL.md +6 -0
- package/.claude/skills/seo-audit/SKILL.md +18 -9
- package/.claude/skills/shadcn-ui/SKILL.md +6 -0
- package/.claude/skills/stitch-design/SKILL.md +7 -0
- package/.claude/skills/systematic-debugging/SKILL.md +21 -0
- package/.claude/skills/tailwind-design-system/SKILL.md +8 -0
- package/.claude/skills/ui-ux-pro-max/SKILL.md +21 -0
- package/.claude/skills/vercel-react-best-practices/SKILL.md +3 -0
- package/.claude/templates/leader.md +1 -1
- package/CHANGELOG.md +31 -0
- package/README.md +3 -8
- package/bin/akili.js +40 -0
- package/docs/cli.md +1 -1
- package/docs/commands/akili-constitution.md +17 -13
- package/docs/flow.md +39 -5
- package/docs/model-routing.md +92 -24
- package/docs/skills/README.md +34 -31
- package/docs/skills/brainstorming.md +2 -0
- package/docs/skills/cognitive-doc-design.md +2 -0
- package/docs/skills/governance.md +77 -0
- package/docs/skills/gsap-animation.md +29 -0
- package/docs/skills/judgment-day.md +2 -0
- package/docs/skills/product-manager-toolkit.md +1 -1
- package/docs/skills/seo-audit.md +2 -0
- package/docs/skills/systematic-debugging.md +2 -0
- package/docs/skills/ui-ux-pro-max.md +2 -0
- package/package.json +1 -1
- package/.claude/skills/gsap-core/SKILL.md +0 -254
- package/.claude/skills/gsap-frameworks/SKILL.md +0 -153
- package/.claude/skills/gsap-plugins/SKILL.md +0 -426
- package/.claude/skills/gsap-react/SKILL.md +0 -136
- package/.claude/skills/gsap-scrolltrigger/SKILL.md +0 -296
- package/.claude/skills/gsap-utils/SKILL.md +0 -284
- package/docs/skills/gsap-core.md +0 -21
- package/docs/skills/gsap-frameworks.md +0 -20
- package/docs/skills/gsap-performance.md +0 -21
- package/docs/skills/gsap-plugins.md +0 -20
- package/docs/skills/gsap-react.md +0 -22
- package/docs/skills/gsap-scrolltrigger.md +0 -21
- package/docs/skills/gsap-timeline.md +0 -20
- 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
|