akili-specs 2.7.0 → 2.8.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 (74) hide show
  1. package/.claude/commands/akili-archive.md +9 -0
  2. package/.claude/commands/akili-audit.md +9 -0
  3. package/.claude/commands/akili-constitution.md +41 -3
  4. package/.claude/commands/akili-execute.md +10 -1
  5. package/.claude/commands/akili-propose.md +9 -0
  6. package/.claude/commands/akili-quick.md +9 -0
  7. package/.claude/commands/akili-resume.md +9 -0
  8. package/.claude/commands/akili-seo.md +9 -0
  9. package/.claude/commands/akili-specify.md +14 -2
  10. package/.claude/commands/akili-test.md +9 -0
  11. package/.claude/commands/akili-validate.md +9 -0
  12. package/.claude/skills/angular-developer/SKILL.md +4 -0
  13. package/.claude/skills/api-design-principles/SKILL.md +8 -0
  14. package/.claude/skills/aws-serverless/SKILL.md +8 -1
  15. package/.claude/skills/brainstorming/SKILL.md +25 -1
  16. package/.claude/skills/cognitive-doc-design/SKILL.md +14 -0
  17. package/.claude/skills/error-handling-patterns/SKILL.md +8 -0
  18. package/.claude/skills/frontend-design/SKILL.md +8 -1
  19. package/.claude/skills/gsap-animation/SKILL.md +182 -0
  20. package/.claude/skills/gsap-animation/references/frameworks.md +137 -0
  21. package/.claude/skills/{gsap-performance/SKILL.md → gsap-animation/references/performance.md} +10 -20
  22. package/.claude/skills/gsap-animation/references/plugins.md +390 -0
  23. package/.claude/skills/gsap-animation/references/react.md +122 -0
  24. package/.claude/skills/gsap-animation/references/scrolltrigger.md +255 -0
  25. package/.claude/skills/{gsap-timeline/SKILL.md → gsap-animation/references/timeline.md} +7 -19
  26. package/.claude/skills/gsap-animation/references/utils.md +254 -0
  27. package/.claude/skills/judgment-day/SKILL.md +19 -1
  28. package/.claude/skills/kaizen/SKILL.md +1 -0
  29. package/.claude/skills/nestjs-expert/SKILL.md +8 -0
  30. package/.claude/skills/product-manager-toolkit/SKILL.md +7 -1
  31. package/.claude/skills/react-doctor/SKILL.md +6 -0
  32. package/.claude/skills/seo-audit/SKILL.md +18 -9
  33. package/.claude/skills/shadcn-ui/SKILL.md +6 -0
  34. package/.claude/skills/stitch-design/SKILL.md +7 -0
  35. package/.claude/skills/systematic-debugging/SKILL.md +21 -0
  36. package/.claude/skills/tailwind-design-system/SKILL.md +8 -0
  37. package/.claude/skills/ui-ux-pro-max/SKILL.md +21 -0
  38. package/.claude/skills/vercel-react-best-practices/SKILL.md +3 -0
  39. package/.claude/templates/implementer.md +6 -0
  40. package/.claude/templates/leader.md +7 -1
  41. package/.claude/templates/reviewer.md +6 -0
  42. package/.claude/templates/tester.md +6 -0
  43. package/CHANGELOG.md +27 -0
  44. package/LICENSE +1 -1
  45. package/README.md +9 -8
  46. package/bin/akili.js +40 -0
  47. package/docs/cli.md +1 -1
  48. package/docs/commands/akili-constitution.md +11 -11
  49. package/docs/flow.md +20 -0
  50. package/docs/skills/README.md +34 -31
  51. package/docs/skills/brainstorming.md +2 -0
  52. package/docs/skills/cognitive-doc-design.md +2 -0
  53. package/docs/skills/governance.md +77 -0
  54. package/docs/skills/gsap-animation.md +29 -0
  55. package/docs/skills/judgment-day.md +2 -0
  56. package/docs/skills/product-manager-toolkit.md +1 -1
  57. package/docs/skills/seo-audit.md +2 -0
  58. package/docs/skills/systematic-debugging.md +2 -0
  59. package/docs/skills/ui-ux-pro-max.md +2 -0
  60. package/package.json +3 -2
  61. package/.claude/skills/gsap-core/SKILL.md +0 -254
  62. package/.claude/skills/gsap-frameworks/SKILL.md +0 -153
  63. package/.claude/skills/gsap-plugins/SKILL.md +0 -426
  64. package/.claude/skills/gsap-react/SKILL.md +0 -136
  65. package/.claude/skills/gsap-scrolltrigger/SKILL.md +0 -296
  66. package/.claude/skills/gsap-utils/SKILL.md +0 -284
  67. package/docs/skills/gsap-core.md +0 -21
  68. package/docs/skills/gsap-frameworks.md +0 -20
  69. package/docs/skills/gsap-performance.md +0 -21
  70. package/docs/skills/gsap-plugins.md +0 -20
  71. package/docs/skills/gsap-react.md +0 -22
  72. package/docs/skills/gsap-scrolltrigger.md +0 -21
  73. package/docs/skills/gsap-timeline.md +0 -20
  74. package/docs/skills/gsap-utils.md +0 -21
@@ -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
@@ -0,0 +1,255 @@
1
+ # GSAP ScrollTrigger
2
+
3
+ Read when implementing scroll-driven animation: triggering tweens/timelines on scroll, pinning, scrubbing, parallax, batching, horizontal scroll, or integrating a smooth-scroll library.
4
+
5
+ ## Registering the Plugin
6
+
7
+ ScrollTrigger is a plugin — register once (see SKILL.md registration pattern):
8
+
9
+ ```javascript
10
+ gsap.registerPlugin(ScrollTrigger);
11
+ ```
12
+
13
+ ## Basic Trigger
14
+
15
+ ```javascript
16
+ gsap.to(".box", {
17
+ x: 500,
18
+ duration: 1,
19
+ scrollTrigger: {
20
+ trigger: ".box",
21
+ start: "top center", // when top of trigger hits center of viewport
22
+ end: "bottom center", // when the bottom of the trigger hits the center of the viewport
23
+ toggleActions: "play reverse play reverse" // onEnter, onLeave, onEnterBack, onLeaveBack
24
+ }
25
+ });
26
+ ```
27
+
28
+ **start** / **end**: `"triggerPosition viewportPosition"`. Examples: `"top top"`, `"center center"`, `"bottom 80%"`, or a numeric pixel value like `500` (scroller scrolls 500px from top). Relative values: `"+=300"`, `"+=100%"` (scroller height past start), or `"max"`. Wrap in **clamp()** (3.12+) to keep within page bounds: `start: "clamp(top bottom)"`. Can also be a **function** returning a string/number (receives the ScrollTrigger instance); call **ScrollTrigger.refresh()** when layout changes.
29
+
30
+ ## Key config options
31
+
32
+ Shorthand `scrollTrigger: ".selector"` sets only `trigger`. Full list: [ScrollTrigger docs](https://gsap.com/docs/v3/Plugins/ScrollTrigger/).
33
+
34
+ | Property | Type | Description |
35
+ |----------|------|-------------|
36
+ | **trigger** | String \| Element | Element whose position defines where the ScrollTrigger starts. Required (or use shorthand). |
37
+ | **start** | String \| Number \| Function | When the trigger becomes active. Default `"top bottom"` (or `"top top"` if `pin: true`). |
38
+ | **end** | String \| Number \| Function | When the trigger ends. Default `"bottom top"`. Use `endTrigger` if end is based on a different element. |
39
+ | **endTrigger** | String \| Element | Element used for **end** when different from trigger. |
40
+ | **scrub** | Boolean \| Number | Link animation progress to scroll. `true` = direct; number = seconds for playhead to "catch up". |
41
+ | **toggleActions** | String | Four actions in order: **onEnter**, **onLeave**, **onEnterBack**, **onLeaveBack**. Each: `"play"`, `"pause"`, `"resume"`, `"reset"`, `"restart"`, `"complete"`, `"reverse"`, `"none"`. Default `"play none none none"`. |
42
+ | **pin** | Boolean \| String \| Element | Pin an element while active. `true` = pin the trigger. Don't animate the pinned element itself; animate children. |
43
+ | **pinSpacing** | Boolean \| String | Default `true` (adds spacer so layout doesn't collapse). `false` or `"margin"`. |
44
+ | **horizontal** | Boolean | `true` for horizontal scrolling. |
45
+ | **scroller** | String \| Element | Scroll container (default: viewport). |
46
+ | **markers** | Boolean \| Object | `true` for dev markers; or `{ startColor, endColor, fontSize, ... }`. Remove in production. |
47
+ | **once** | Boolean | If `true`, kills the ScrollTrigger after end is reached once (animation keeps running). |
48
+ | **id** | String | Unique id for **ScrollTrigger.getById(id)**. |
49
+ | **refreshPriority** | Number | Lower = refreshed first. Set when creating ScrollTriggers out of page order so they refresh in page order (first on page = lower number). |
50
+ | **toggleClass** | String \| Object | Add/remove class when active. String = on trigger; or `{ targets: ".x", className: "active" }`. |
51
+ | **snap** | Number \| Array \| Function \| "labels" \| Object | Snap to progress values. Number = increments (e.g. `0.25`); array = specific values; `"labels"` = timeline labels; object: `{ snapTo: 0.25, duration: 0.3, delay: 0.1, ease: "power1.inOut" }`. |
52
+ | **containerAnimation** | Tween \| Timeline | For "fake" horizontal scroll (see below). Pinning and snapping are not available on containerAnimation-based ScrollTriggers. |
53
+ | **onEnter**, **onLeave**, **onEnterBack**, **onLeaveBack** | Function | Callbacks when crossing start/end; receive the ScrollTrigger instance (`progress`, `direction`, `isActive`, `getVelocity()`). |
54
+ | **onUpdate**, **onToggle**, **onRefresh**, **onScrubComplete** | Function | **onUpdate** fires when progress changes; **onToggle** when active flips; **onRefresh** after recalc; **onScrubComplete** when numeric scrub finishes. |
55
+
56
+ **Standalone ScrollTrigger** (no linked tween): use **ScrollTrigger.create()** with the same config and callbacks for custom behavior.
57
+
58
+ ```javascript
59
+ ScrollTrigger.create({
60
+ trigger: "#id",
61
+ start: "top top",
62
+ end: "bottom 50%+=100px",
63
+ onUpdate: (self) => console.log(self.progress.toFixed(3), self.direction)
64
+ });
65
+ ```
66
+
67
+ ## ScrollTrigger.batch()
68
+
69
+ **ScrollTrigger.batch(triggers, vars)** creates one ScrollTrigger per target and **batches** their callbacks (onEnter, onLeave, etc.) within a short interval — e.g. animate every element that just entered the viewport in one staggered go. Good alternative to IntersectionObserver. Returns an Array of ScrollTrigger instances.
70
+
71
+ - **triggers**: selector text (e.g. `".box"`) or Array of elements.
72
+ - **vars**: standard ScrollTrigger config (start, end, once, callbacks). Do **not** pass `trigger` or animation-related options: `animation`, `invalidateOnRefresh`, `onSnapComplete`, `onScrubComplete`, `scrub`, `snap`, `toggleActions`.
73
+
74
+ **Callback signature:** Batched callbacks receive **two** parameters (unlike normal callbacks):
75
+ 1. **targets** — Array of trigger elements that fired this callback within the interval.
76
+ 2. **scrollTriggers** — Array of the ScrollTrigger instances that fired.
77
+
78
+ **Batch options in vars:**
79
+ - **interval** (Number) — Max seconds to collect each batch. Default ≈ one requestAnimationFrame.
80
+ - **batchMax** (Number | Function) — Max elements per batch. Use a **function** returning a number for responsive layouts; it runs on refresh.
81
+
82
+ ```javascript
83
+ ScrollTrigger.batch(".box", {
84
+ onEnter: (elements, triggers) => {
85
+ gsap.to(elements, { opacity: 1, y: 0, stagger: 0.15 });
86
+ },
87
+ onLeave: (elements, triggers) => {
88
+ gsap.to(elements, { opacity: 0, y: 100 });
89
+ },
90
+ start: "top 80%",
91
+ end: "bottom 20%"
92
+ });
93
+ ```
94
+
95
+ ```javascript
96
+ ScrollTrigger.batch(".card", {
97
+ interval: 0.1,
98
+ batchMax: 4,
99
+ onEnter: (batch) => gsap.to(batch, { opacity: 1, y: 0, stagger: 0.1, overwrite: true }),
100
+ onLeaveBack: (batch) => gsap.set(batch, { opacity: 0, y: 50, overwrite: true })
101
+ });
102
+ ```
103
+
104
+ See [ScrollTrigger.batch()](https://gsap.com/docs/v3/Plugins/ScrollTrigger/static.batch/).
105
+
106
+ ## ScrollTrigger.scrollerProxy()
107
+
108
+ **ScrollTrigger.scrollerProxy(scroller, vars)** overrides how ScrollTrigger reads/writes scroll position for a scroller. Use when integrating a third-party smooth-scroll (or custom scroll) library. GSAP's **ScrollSmoother** is the built-in option and needs no proxy.
109
+
110
+ - **scroller**: selector or element (e.g. `"body"`, `".container"`).
111
+ - **vars**: object with **scrollTop** and/or **scrollLeft** functions. Each is both getter and setter: called **with** an argument = setter; **with no** argument = getter. At least one is required.
112
+
113
+ **Optional in vars:**
114
+ - **getBoundingClientRect** — Function returning `{ top, left, width, height }` for the scroller.
115
+ - **scrollWidth** / **scrollHeight** — Getter/setter functions when the library exposes different dimensions.
116
+ - **fixedMarkers** (Boolean) — When `true`, markers are treated as `position: fixed` (useful when the scroller is translated).
117
+ - **pinType** — `"fixed"` or `"transform"`. Use `"fixed"` if pins jitter; `"transform"` if pins don't stick.
118
+
119
+ **Critical:** When the third-party scroller updates, notify ScrollTrigger — register **ScrollTrigger.update** as a listener (e.g. `smoothScroller.addListener(ScrollTrigger.update)`), else calculations go stale.
120
+
121
+ ```javascript
122
+ ScrollTrigger.scrollerProxy(document.body, {
123
+ scrollTop(value) {
124
+ if (arguments.length) scrollbar.scrollTop = value;
125
+ return scrollbar.scrollTop;
126
+ },
127
+ getBoundingClientRect() {
128
+ return { top: 0, left: 0, width: window.innerWidth, height: window.innerHeight };
129
+ }
130
+ });
131
+ scrollbar.addListener(ScrollTrigger.update);
132
+ ```
133
+
134
+ See [ScrollTrigger.scrollerProxy()](https://gsap.com/docs/v3/Plugins/ScrollTrigger/static.scrollerProxy/).
135
+
136
+ ## Scrub
137
+
138
+ Scrub ties animation progress to scroll. Use for the "scroll-driven" feel:
139
+
140
+ ```javascript
141
+ gsap.to(".box", {
142
+ x: 500,
143
+ scrollTrigger: {
144
+ trigger: ".box",
145
+ start: "top center",
146
+ end: "bottom center",
147
+ scrub: true // or a number (smoothness lag in seconds): 0.5 takes 0.5s to catch up
148
+ }
149
+ });
150
+ ```
151
+
152
+ ## Pinning
153
+
154
+ ```javascript
155
+ scrollTrigger: {
156
+ trigger: ".section",
157
+ start: "top top",
158
+ end: "+=1000", // pin for 1000px scroll
159
+ pin: true,
160
+ scrub: 1
161
+ }
162
+ ```
163
+
164
+ **pinSpacing** — default `true`; adds a spacer so layout doesn't collapse when the pinned element becomes `position: fixed`. Set `pinSpacing: false` only when layout is handled separately.
165
+
166
+ ## Markers (Development)
167
+
168
+ ```javascript
169
+ scrollTrigger: { trigger: ".box", start: "top center", end: "bottom center", markers: true }
170
+ ```
171
+
172
+ Remove or set **markers: false** for production.
173
+
174
+ ## Timeline + ScrollTrigger
175
+
176
+ Drive a timeline with scroll and optional scrub — put ScrollTrigger on the **timeline**, not a child tween:
177
+
178
+ ```javascript
179
+ const tl = gsap.timeline({
180
+ scrollTrigger: { trigger: ".container", start: "top top", end: "+=2000", scrub: 1, pin: true }
181
+ });
182
+ tl.to(".a", { x: 100 }).to(".b", { y: 50 }).to(".c", { opacity: 0 });
183
+ ```
184
+
185
+ ## Horizontal scroll (containerAnimation)
186
+
187
+ Pin a section, then as the user scrolls **vertically**, content inside moves **horizontally** ("fake" horizontal scroll). Animate **x**/**xPercent** of an element *inside* the pinned trigger, tie it to vertical scroll, and use **containerAnimation** so ScrollTrigger monitors the horizontal animation's progress.
188
+
189
+ **Critical:** The horizontal tween/timeline **must** use **ease: "none"**, or scroll position and horizontal position won't line up — a very common mistake.
190
+
191
+ 1. Pin the section (trigger = the full-viewport panel).
192
+ 2. Build a tween animating inner content's **x**/**xPercent** with **ease: "none"**.
193
+ 3. Attach ScrollTrigger to that tween with **pin: true**, **scrub: true**.
194
+ 4. For things triggered by the horizontal movement, set **containerAnimation** to that tween.
195
+
196
+ ```javascript
197
+ const scrollingEl = document.querySelector(".horizontal-el");
198
+ const scrollTween = gsap.to(scrollingEl, {
199
+ xPercent: () => Math.max(0, window.innerWidth - scrollingEl.offsetWidth),
200
+ ease: "none", // required
201
+ scrollTrigger: {
202
+ trigger: scrollingEl,
203
+ pin: scrollingEl.parentNode, // wrapper so we're not animating the pinned element
204
+ start: "top top",
205
+ end: "+=1000"
206
+ }
207
+ });
208
+
209
+ gsap.to(".nested-el-1", {
210
+ y: 100,
211
+ scrollTrigger: {
212
+ containerAnimation: scrollTween, // IMPORTANT
213
+ trigger: ".nested-wrapper-1",
214
+ start: "left center", // based on horizontal movement
215
+ toggleActions: "play none none reset"
216
+ }
217
+ });
218
+ ```
219
+
220
+ **Caveats:** Pinning and snapping are not available on containerAnimation ScrollTriggers. The container animation must use **ease: "none"**. Animate a child, not the trigger element itself. If the trigger is moved, offset **start**/**end** accordingly.
221
+
222
+ ## Refresh and Cleanup
223
+
224
+ - **ScrollTrigger.refresh()** — recalculate positions after DOM/layout changes (new content, images, fonts). Auto-called on viewport resize (debounced 200ms). Refresh runs in creation order (or by **refreshPriority**); create ScrollTriggers top-to-bottom or set **refreshPriority** so they refresh in page order.
225
+ - When removing elements or changing pages (SPAs), **kill** stale ScrollTriggers:
226
+
227
+ ```javascript
228
+ ScrollTrigger.getAll().forEach(t => t.kill());
229
+ ScrollTrigger.getById("my-id")?.kill();
230
+ ```
231
+
232
+ In React, use `useGSAP()` (@gsap/react) for automatic cleanup, or kill manually in a cleanup — see `references/react.md`.
233
+
234
+ ## Best practices
235
+
236
+ - ✅ **gsap.registerPlugin(ScrollTrigger)** once before any use.
237
+ - ✅ Call **ScrollTrigger.refresh()** after DOM/layout changes that affect trigger positions (viewport resize is auto-handled, debounced 200ms; dynamic content is not).
238
+ - ✅ In React, use `useGSAP()`; elsewhere use `gsap.context()` + revert in cleanup.
239
+ - ✅ Use **scrub** for scroll-linked progress OR **toggleActions** for discrete play/reverse — not both on the same trigger (scrub wins).
240
+ - ✅ For fake horizontal scroll with **containerAnimation**, use **ease: "none"**.
241
+ - ✅ Create ScrollTriggers top-to-bottom (scroll 0 → max), or set **refreshPriority** when created out of order.
242
+
243
+ ## Do Not
244
+
245
+ - ❌ Put ScrollTrigger on a **child tween** of a timeline; put it on the **timeline** or a **top-level tween**. Wrong: `gsap.timeline().to(".a", { scrollTrigger: {...} })`. Correct: `gsap.timeline({ scrollTrigger: {...} }).to(".a", { x: 100 })`.
246
+ - ❌ Forget **ScrollTrigger.refresh()** after DOM/layout changes.
247
+ - ❌ Nest ScrollTriggered animations inside a parent timeline.
248
+ - ❌ Forget **gsap.registerPlugin(ScrollTrigger)**.
249
+ - ❌ Use **scrub** and **toggleActions** together on the same ScrollTrigger.
250
+ - ❌ Use an ease other than **"none"** on the horizontal animation with **containerAnimation**.
251
+ - ❌ Create ScrollTriggers in random/async order without setting **refreshPriority**.
252
+ - ❌ Leave **markers: true** in production.
253
+
254
+ ### Learn More
255
+ https://gsap.com/docs/v3/Plugins/ScrollTrigger/
@@ -1,16 +1,6 @@
1
- ---
2
- name: gsap-timeline
3
- description: Official GSAP skill for timelines — gsap.timeline(), position parameter, nesting, playback. Use when sequencing animations, choreographing keyframes, or when the user asks about animation sequencing, timelines, or animation order (in GSAP or when recommending a library that supports timelines).
4
- license: MIT
5
- ---
6
-
7
1
  # GSAP Timeline
8
2
 
9
- ## When to Use This Skill
10
-
11
- Apply when building multi-step animations, coordinating several tweens in sequence or parallel, or when the user asks about timelines, sequencing, or keyframe-style animation in GSAP.
12
-
13
- **Related skills:** For single tweens and eases use **gsap-core**; for scroll-driven timelines use **gsap-scrolltrigger**; for React use **gsap-react**.
3
+ Read when sequencing multiple tweens, choreographing keyframes, or the task mentions timelines, sequencing, or animation order.
14
4
 
15
5
  ## Creating a Timeline
16
6
 
@@ -25,15 +15,13 @@ By default, tweens are **appended** one after another. Use the **position parame
25
15
 
26
16
  ## Position Parameter
27
17
 
28
- Third argument (or position property in vars) controls placement:
18
+ Third argument (or `position` property in vars) controls placement:
29
19
 
30
20
  - **Absolute**: `1` — start at 1 second.
31
21
  - **Relative (default)**: `"+=0.5"` — 0.5s after end; `"-=0.2"` — 0.2s before end.
32
22
  - **Label**: `"labelName"` — at that label; `"labelName+=0.3"` — 0.3s after label.
33
23
  - **Placement**: `"<"` — start when recently-added animation starts; `">"` — start when recently-added animation ends (default); `"<0.2"` — 0.2s after recently-added animation start.
34
24
 
35
- Examples:
36
-
37
25
  ```javascript
38
26
  tl.to(".a", { x: 100 }, 0); // at 0
39
27
  tl.to(".b", { y: 50 }, "+=0.5"); // 0.5s after last end
@@ -53,7 +41,7 @@ tl.to(".a", { x: 100 }).to(".b", { y: 50 }); // both use 0.5s and power2.out
53
41
  ## Timeline Options (constructor)
54
42
 
55
43
  - **paused: true** — create paused; call `.play()` to start.
56
- - **repeat**, **yoyo** — same as tweens; apply to whole timeline.
44
+ - **repeat**, **yoyo** — same as tweens; apply to the whole timeline.
57
45
  - **onComplete**, **onStart**, **onUpdate** — timeline-level callbacks.
58
46
  - **defaults** — vars merged into every child tween.
59
47
 
@@ -67,7 +55,7 @@ tl.to(".a", { x: 100 }, "intro");
67
55
  tl.addLabel("outro", "+=0.5");
68
56
  tl.to(".b", { opacity: 0 }, "outro");
69
57
  tl.play("outro"); // start from "outro"
70
- tl.tweenFromTo("intro", "outro"); // pauses the timeline and returns a new Tween that animates the timeline's playhead from intro to outro with no ease.
58
+ tl.tweenFromTo("intro", "outro"); // pauses the timeline; returns a new Tween that animates the playhead from intro to outro with no ease.
71
59
  ```
72
60
 
73
61
  ## Nesting Timelines
@@ -91,9 +79,9 @@ master.to(".c", { opacity: 0 }, "+=0.2");
91
79
  - **tl.progress(0.5)** — seek to 50%.
92
80
  - **tl.kill()** — kill timeline and (by default) its children.
93
81
 
94
- ## Official GSAP Best practices
82
+ ## Best practices
95
83
 
96
- - ✅ Prefer timelines for sequencing
84
+ - ✅ Prefer timelines for sequencing.
97
85
  - ✅ Use the **position parameter** (third argument) to place tweens at specific times or relative to labels.
98
86
  - ✅ Add **labels** with `addLabel()` for readable, maintainable sequencing.
99
87
  - ✅ Pass **defaults** into the timeline constructor so child tweens inherit duration, ease, etc.
@@ -103,5 +91,5 @@ master.to(".c", { opacity: 0 }, "+=0.2");
103
91
 
104
92
  - ❌ Chain animations with **delay** when a **timeline** can sequence them; prefer `gsap.timeline()` and the position parameter for multi-step animation.
105
93
  - ❌ Forget to pass **defaults** (e.g. `defaults: { duration: 0.5, ease: "power2.out" }`) when many child tweens share the same duration or ease.
106
- - ❌ Forget that **duration** on the timeline constructor is not the same as tween duration; timeline duration is determined by its children.
94
+ - ❌ Confuse **duration** on the timeline constructor with tween duration; a timeline's "duration" is determined by its children.
107
95
  - ❌ Nest animations that contain a ScrollTrigger; ScrollTriggers should only be on top-level Tweens/Timelines.