akili-specs 2.7.1 → 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 (62) hide show
  1. package/.claude/commands/akili-constitution.md +32 -3
  2. package/.claude/commands/akili-execute.md +1 -1
  3. package/.claude/commands/akili-specify.md +5 -2
  4. package/.claude/skills/angular-developer/SKILL.md +4 -0
  5. package/.claude/skills/api-design-principles/SKILL.md +8 -0
  6. package/.claude/skills/aws-serverless/SKILL.md +8 -1
  7. package/.claude/skills/brainstorming/SKILL.md +25 -1
  8. package/.claude/skills/cognitive-doc-design/SKILL.md +14 -0
  9. package/.claude/skills/error-handling-patterns/SKILL.md +8 -0
  10. package/.claude/skills/frontend-design/SKILL.md +8 -1
  11. package/.claude/skills/gsap-animation/SKILL.md +182 -0
  12. package/.claude/skills/gsap-animation/references/frameworks.md +137 -0
  13. package/.claude/skills/{gsap-performance/SKILL.md → gsap-animation/references/performance.md} +10 -20
  14. package/.claude/skills/gsap-animation/references/plugins.md +390 -0
  15. package/.claude/skills/gsap-animation/references/react.md +122 -0
  16. package/.claude/skills/gsap-animation/references/scrolltrigger.md +255 -0
  17. package/.claude/skills/{gsap-timeline/SKILL.md → gsap-animation/references/timeline.md} +7 -19
  18. package/.claude/skills/gsap-animation/references/utils.md +254 -0
  19. package/.claude/skills/judgment-day/SKILL.md +19 -1
  20. package/.claude/skills/kaizen/SKILL.md +1 -0
  21. package/.claude/skills/nestjs-expert/SKILL.md +8 -0
  22. package/.claude/skills/product-manager-toolkit/SKILL.md +7 -1
  23. package/.claude/skills/react-doctor/SKILL.md +6 -0
  24. package/.claude/skills/seo-audit/SKILL.md +18 -9
  25. package/.claude/skills/shadcn-ui/SKILL.md +6 -0
  26. package/.claude/skills/stitch-design/SKILL.md +7 -0
  27. package/.claude/skills/systematic-debugging/SKILL.md +21 -0
  28. package/.claude/skills/tailwind-design-system/SKILL.md +8 -0
  29. package/.claude/skills/ui-ux-pro-max/SKILL.md +21 -0
  30. package/.claude/skills/vercel-react-best-practices/SKILL.md +3 -0
  31. package/.claude/templates/leader.md +1 -1
  32. package/CHANGELOG.md +19 -0
  33. package/README.md +3 -8
  34. package/bin/akili.js +40 -0
  35. package/docs/cli.md +1 -1
  36. package/docs/commands/akili-constitution.md +11 -11
  37. package/docs/flow.md +20 -0
  38. package/docs/skills/README.md +34 -31
  39. package/docs/skills/brainstorming.md +2 -0
  40. package/docs/skills/cognitive-doc-design.md +2 -0
  41. package/docs/skills/governance.md +77 -0
  42. package/docs/skills/gsap-animation.md +29 -0
  43. package/docs/skills/judgment-day.md +2 -0
  44. package/docs/skills/product-manager-toolkit.md +1 -1
  45. package/docs/skills/seo-audit.md +2 -0
  46. package/docs/skills/systematic-debugging.md +2 -0
  47. package/docs/skills/ui-ux-pro-max.md +2 -0
  48. package/package.json +1 -1
  49. package/.claude/skills/gsap-core/SKILL.md +0 -254
  50. package/.claude/skills/gsap-frameworks/SKILL.md +0 -153
  51. package/.claude/skills/gsap-plugins/SKILL.md +0 -426
  52. package/.claude/skills/gsap-react/SKILL.md +0 -136
  53. package/.claude/skills/gsap-scrolltrigger/SKILL.md +0 -296
  54. package/.claude/skills/gsap-utils/SKILL.md +0 -284
  55. package/docs/skills/gsap-core.md +0 -21
  56. package/docs/skills/gsap-frameworks.md +0 -20
  57. package/docs/skills/gsap-performance.md +0 -21
  58. package/docs/skills/gsap-plugins.md +0 -20
  59. package/docs/skills/gsap-react.md +0 -22
  60. package/docs/skills/gsap-scrolltrigger.md +0 -21
  61. package/docs/skills/gsap-timeline.md +0 -20
  62. package/docs/skills/gsap-utils.md +0 -21
@@ -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.
@@ -0,0 +1,254 @@
1
+ # gsap.utils
2
+
3
+ Read when using **gsap.utils** for math, array/collection handling, unit parsing, or value mapping in animations (e.g. mapping scroll to a value, randomizing, snapping to a grid, normalizing inputs).
4
+
5
+ ## Overview
6
+
7
+ **gsap.utils** provides pure helpers; no registration needed. Use in tween vars (function-based values), in ScrollTrigger or Observer callbacks, or any JS that drives GSAP. All are on **gsap.utils** (e.g. `gsap.utils.clamp()`).
8
+
9
+ **Omitting the value: function form.** Many utils accept the value to transform as the **last** argument. Omit it and the util returns a **function** that accepts the value later — use this when clamping/mapping/normalizing/snapping many values with the same config (e.g. a mousemove handler). **Exception: random()** — pass **true** as the last argument for a reusable function (do not omit the value).
10
+
11
+ ```javascript
12
+ // With value: returns the result
13
+ gsap.utils.clamp(0, 100, 150); // 100
14
+
15
+ // Without value: returns a function you call later
16
+ let c = gsap.utils.clamp(0, 100);
17
+ c(150); // 100
18
+ c(-10); // 0
19
+ ```
20
+
21
+ ## Clamping and Ranges
22
+
23
+ ### clamp(min, max, value?)
24
+
25
+ Constrains a value between min and max.
26
+
27
+ ```javascript
28
+ gsap.utils.clamp(0, 100, 150); // 100
29
+ gsap.utils.clamp(0, 100, -10); // 0
30
+
31
+ let clampFn = gsap.utils.clamp(0, 100);
32
+ clampFn(150); // 100
33
+ ```
34
+
35
+ ### mapRange(inMin, inMax, outMin, outMax, value?)
36
+
37
+ Maps a value from one range to another. Use when converting scroll position, progress (0–1), or input range to an animation range.
38
+
39
+ ```javascript
40
+ gsap.utils.mapRange(0, 100, 0, 500, 50); // 250
41
+ gsap.utils.mapRange(0, 1, 0, 360, 0.5); // 180 (progress to degrees)
42
+
43
+ let mapFn = gsap.utils.mapRange(0, 100, 0, 500);
44
+ mapFn(50); // 250
45
+ ```
46
+
47
+ ### normalize(min, max, value?)
48
+
49
+ Returns a value normalized to 0–1 for the given range.
50
+
51
+ ```javascript
52
+ gsap.utils.normalize(0, 100, 50); // 0.5
53
+ gsap.utils.normalize(100, 300, 200); // 0.5
54
+ ```
55
+
56
+ ### interpolate(start, end, progress?)
57
+
58
+ Interpolates between two values at a given progress (0–1). Handles numbers, colors, and objects with matching keys.
59
+
60
+ ```javascript
61
+ gsap.utils.interpolate(0, 100, 0.5); // 50
62
+ gsap.utils.interpolate("#ff0000", "#0000ff", 0.5); // mid color
63
+ gsap.utils.interpolate({ x: 0, y: 0 }, { x: 100, y: 50 }, 0.5); // { x: 50, y: 25 }
64
+
65
+ let lerp = gsap.utils.interpolate(0, 100);
66
+ lerp(0.5); // 50
67
+ ```
68
+
69
+ ## Random and Snap
70
+
71
+ ### random(minimum, maximum[, snapIncrement, returnFunction]) / random(array[, returnFunction])
72
+
73
+ Returns a random number in the range, or a random element from an **array**. Optional **snapIncrement** snaps to the nearest multiple. **For a reusable function, pass true as the last argument** (returnFunction) — the only util that uses `true` instead of omitting the value.
74
+
75
+ ```javascript
76
+ gsap.utils.random(-100, 100); // e.g. 42.7
77
+ gsap.utils.random(0, 500, 5); // 0–500, snapped to nearest 5
78
+
79
+ let randomFn = gsap.utils.random(-200, 500, 10, true);
80
+ randomFn(); // random value in range, snapped to 10
81
+
82
+ gsap.utils.random(["red", "blue", "green"]); // one at random
83
+ let randomFromArray = gsap.utils.random([0, 100, 200], true);
84
+ randomFromArray(); // 0, 100, or 200
85
+ ```
86
+
87
+ **String form in tween vars:** `"random(-100, 100)"`, `"random(-100, 100, 5)"`, or `"random([0, 100, 200])"`; GSAP evaluates per target.
88
+
89
+ ```javascript
90
+ gsap.to(".box", { x: "random(-100, 100, 5)", duration: 1 });
91
+ gsap.to(".item", { backgroundColor: "random([red, blue, green])" });
92
+ ```
93
+
94
+ ### snap(snapTo, value?)
95
+
96
+ Snaps to the nearest multiple of **snapTo**, or to the nearest value in an array.
97
+
98
+ ```javascript
99
+ gsap.utils.snap(10, 23); // 20
100
+ gsap.utils.snap(0.25, 0.7); // 0.75
101
+ gsap.utils.snap([0, 100, 200], 150); // 100 or 200
102
+
103
+ let snapFn = gsap.utils.snap(10);
104
+ snapFn(23); // 20
105
+ ```
106
+
107
+ In tweens for grid/step-based animation:
108
+
109
+ ```javascript
110
+ gsap.to(".x", { x: 200, snap: { x: 20 } });
111
+ ```
112
+
113
+ ### shuffle(array)
114
+
115
+ Returns a new array with the same elements in random order.
116
+
117
+ ```javascript
118
+ gsap.utils.shuffle([1, 2, 3, 4]); // e.g. [3, 1, 4, 2]
119
+ ```
120
+
121
+ ### distribute(config)
122
+
123
+ **Returns a function** that assigns a value to each target based on its position in the array (or grid). Used internally for advanced staggers; use whenever values must spread across many elements (scale, opacity, x, delay). The returned function receives `(index, target, targets)` — call it manually or pass it directly into a tween.
124
+
125
+ **Config (all optional):**
126
+
127
+ | Property | Type | Description |
128
+ |----------|------|-------------|
129
+ | `base` | Number | Starting value. Default `0`. |
130
+ | `amount` | Number | Total distributed across all targets (added to base). E.g. `amount: 1` with 100 targets → 0.01 between each. |
131
+ | `each` | Number | Amount added between each target. E.g. `each: 1` with 4 targets → 0, 1, 2, 3. |
132
+ | `from` | Number \| String \| Array | Where distribution starts: index, `"start"`, `"center"`, `"edges"`, `"random"`, `"end"`, or ratios like `[0.25, 0.75]`. Default `0`. |
133
+ | `grid` | String \| Array | Grid position instead of flat index: `[rows, columns]` or `"auto"`. |
134
+ | `axis` | String | For grid: limit to one axis (`"x"` or `"y"`). |
135
+ | `ease` | Ease | Distribute along an ease curve. Default `"none"`. |
136
+
137
+ ```javascript
138
+ // Scale: middle elements 0.5, outer edges 3 (amount 2.5 from center)
139
+ gsap.to(".class", {
140
+ scale: gsap.utils.distribute({ base: 0.5, amount: 2.5, from: "center" })
141
+ });
142
+ ```
143
+
144
+ **Manual use:** call the returned function with `(index, target, targets)`.
145
+
146
+ ```javascript
147
+ const distributor = gsap.utils.distribute({ base: 50, amount: 100, from: "center", ease: "power1.inOut" });
148
+ const targets = gsap.utils.toArray(".box");
149
+ const valueForIndex2 = distributor(2, targets[2], targets);
150
+ ```
151
+
152
+ See [distribute()](https://gsap.com/docs/v3/GSAP/UtilityMethods/distribute/).
153
+
154
+ ## Units and Parsing
155
+
156
+ ### getUnit(value)
157
+
158
+ Returns the unit string of a value (`"px"`, `"%"`, `"deg"`).
159
+
160
+ ```javascript
161
+ gsap.utils.getUnit("100px"); // "px"
162
+ gsap.utils.getUnit("50%"); // "%"
163
+ gsap.utils.getUnit(42); // "" (unitless)
164
+ ```
165
+
166
+ ### unitize(value, unit)
167
+
168
+ Appends a unit to a number, or returns the value unchanged if it already has one.
169
+
170
+ ```javascript
171
+ gsap.utils.unitize(100, "px"); // "100px"
172
+ gsap.utils.unitize("2rem", "px"); // "2rem" (unchanged)
173
+ ```
174
+
175
+ ### splitColor(color, returnHSL?)
176
+
177
+ Converts a color string into an array: **[r, g, b]** (0–255) or **[r, g, b, a]** for RGBA. Pass **true** as the second argument (returnHSL) to get **[h, s, l]** / **[h, s, l, a]**. Works with `rgb()`, `rgba()`, `hsl()`, `hsla()`, hex, and named colors.
178
+
179
+ ```javascript
180
+ gsap.utils.splitColor("red"); // [255, 0, 0]
181
+ gsap.utils.splitColor("#6fb936"); // [111, 185, 54]
182
+ gsap.utils.splitColor("rgba(204, 153, 51, 0.5)"); // [204, 153, 51, 0.5]
183
+ gsap.utils.splitColor("#6fb936", true); // [94, 55, 47] (HSL)
184
+ ```
185
+
186
+ See [splitColor()](https://gsap.com/docs/v3/GSAP/UtilityMethods/splitColor/).
187
+
188
+ ## Arrays and Collections
189
+
190
+ ### selector(scope)
191
+
192
+ Returns a scoped selector function that finds elements only within the given element (or ref). Use in components so `".box"` matches only descendants. Accepts a DOM element or a ref (handles `.current`).
193
+
194
+ ```javascript
195
+ const q = gsap.utils.selector(containerRef);
196
+ q(".box"); // array of .box elements inside container
197
+ gsap.to(q(".circle"), { x: 100 });
198
+ ```
199
+
200
+ ### toArray(value, scope?)
201
+
202
+ Converts a value to an array: selector string (scoped to element), NodeList, HTMLCollection, single element, or array.
203
+
204
+ ```javascript
205
+ gsap.utils.toArray(".item"); // array of elements
206
+ gsap.utils.toArray(".item", container); // scoped to container
207
+ gsap.utils.toArray(nodeList); // [ ... ] from NodeList
208
+ ```
209
+
210
+ ### pipe(...functions)
211
+
212
+ Composes functions: **pipe(f1, f2, f3)(value)** returns f3(f2(f1(value))). Use for a chain of transforms (e.g. normalize → mapRange → snap).
213
+
214
+ ```javascript
215
+ const fn = gsap.utils.pipe(
216
+ (v) => gsap.utils.normalize(0, 100, v),
217
+ (v) => gsap.utils.snap(0.1, v)
218
+ );
219
+ fn(50); // normalized then snapped
220
+ ```
221
+
222
+ ### wrap(min, max, value?)
223
+
224
+ Wraps a value into the range min–max (inclusive min, exclusive max). Use for infinite scroll or cyclic values.
225
+
226
+ ```javascript
227
+ gsap.utils.wrap(0, 360, 370); // 10
228
+ gsap.utils.wrap(0, 360, -10); // 350
229
+
230
+ let wrapFn = gsap.utils.wrap(0, 360);
231
+ wrapFn(370); // 10
232
+ ```
233
+
234
+ ### wrapYoyo(min, max, value?)
235
+
236
+ Wraps a value in range with a yoyo (bounces at ends).
237
+
238
+ ```javascript
239
+ gsap.utils.wrapYoyo(0, 100, 150); // 50 (bounces back)
240
+ ```
241
+
242
+ ## Best practices
243
+
244
+ - ✅ Omit the value argument to get a reusable function when the same range/config is used many times (e.g. `let mapFn = gsap.utils.mapRange(0, 1, 0, 360); mapFn(progress)`).
245
+ - ✅ Use **snap** for grid-aligned or step-based values; use **toArray** when a real array is needed from a selector or NodeList.
246
+ - ✅ Use **gsap.utils.selector(scope)** in components so selectors are scoped to a container or ref.
247
+
248
+ ## Do Not
249
+
250
+ - ❌ Assume **mapRange** / **normalize** handle units; they work on numbers. Use **getUnit** / **unitize** when units matter.
251
+ - ❌ Override or rely on undocumented behavior; stick to the documented API.
252
+
253
+ ### Learn More
254
+ https://gsap.com/docs/v3/HelperFunctions
@@ -4,6 +4,9 @@ description: "Trigger: judgment day, dual review, adversarial review, juzgar. Ru
4
4
  license: Apache-2.0
5
5
  metadata:
6
6
  author: gentleman-programming
7
+ adapted-by: "Juan Carlos Cadavid — jcadavid.com"
8
+ adapted-for: "AKILI-SPECS"
9
+ binding: core
7
10
  version: "1.6"
8
11
  ---
9
12
 
@@ -49,4 +52,19 @@ Return target identity, round, confirmed/suspect/contradiction/INFO counts, corr
49
52
  ## References
50
53
 
51
54
  - [../_shared/review-ledger-contract.md](../_shared/review-ledger-contract.md) — canonical transaction, ledger, persistence, and lifecycle contract.
52
- - [references/prompts-and-formats.md](references/prompts-and-formats.md) — compact judge/fix prompts and verdict shape.
55
+ - [references/prompts-and-formats.md](references/prompts-and-formats.md) — compact judge/fix prompts and verdict shape.
56
+
57
+ These reference files are not packaged with AKILI-SPECS. When they are unavailable, proceed with the contract described in this document alone: persist the findings ledger inside the spec folder (see below) and derive judge prompts from the Hard Rules and Output Contract.
58
+
59
+ ## AKILI-SPECS Integration
60
+
61
+ | AKILI moment | How to use this skill |
62
+ |---|---|
63
+ | `/akili-specify` Step 2.3 — **Review Design** option | The user-selected blind dual review of `design.md` before tasks are written; the target is the approved requirements + draft design |
64
+ | `/akili-archive` Kaizen Measure | Severe confirmed findings from Judgment Day runs are a signal row in the `kaizen` retrospective |
65
+
66
+ Adaptation rules:
67
+
68
+ - Persist the findings ledger as `docs/specs/<spec-path>/judgment.md` so `/akili-archive` can read it as evidence.
69
+ - The two-judge blind protocol, the two-round fix ceiling, and the `APPROVED`/`ESCALATED` terminal states apply unchanged.
70
+ - Prefer running the two judges on a model different from the one that authored the design (author ≠ auditor, per AKILI model routing).
@@ -4,6 +4,7 @@ description: "Trigger: kaizen, retrospective, continuous improvement, mejora con
4
4
  license: MIT
5
5
  metadata:
6
6
  author: Juan Carlos Cadavid — jcadavid.com
7
+ binding: core
7
8
  version: "1.0"
8
9
  inspired-by: "Kaizen Institute glossary (kaizen.com), 'El Método Kaizen' (small-steps approach, Robert Maurer), 'Emprendiendo Kaizen' (INTI, 2019, ISBN 978-950-532-415-6)"
9
10
  ---
@@ -4,6 +4,14 @@ description: Nest.js framework expert specializing in module architecture, depen
4
4
  category: framework
5
5
  displayName: Nest.js Framework Expert
6
6
  color: red
7
+ license: MIT
8
+ metadata:
9
+ author: Daniel Avila (davila7)
10
+ source: https://github.com/davila7/claude-code-templates
11
+ adapted-by: "Juan Carlos Cadavid — jcadavid.com"
12
+ adapted-for: "AKILI-SPECS"
13
+ binding: stack
14
+ version: "1.0"
7
15
  ---
8
16
 
9
17
  # Nest.js Expert
@@ -1,8 +1,14 @@
1
1
  ---
2
2
  name: product-manager-toolkit
3
3
  description: Comprehensive toolkit for product managers including RICE prioritization, customer interview analysis, PRD templates, discovery frameworks, and go-to-market strategies. Use for feature prioritization, user research synthesis, requirement documentation, and product strategy development. In AKILI-SPECS projects, follow the AKILI-SPECS Integration section first.
4
+ license: MIT
4
5
  metadata:
5
- adapted-for: "AKILI-SPECS methodology by Juan Carlos Cadavid — jcadavid.com"
6
+ author: Alireza Rezvani (alirezarezvani)
7
+ source: https://github.com/alirezarezvani/claude-skills
8
+ adapted-by: "Juan Carlos Cadavid — jcadavid.com"
9
+ adapted-for: "AKILI-SPECS"
10
+ binding: core
11
+ version: "1.0"
6
12
  ---
7
13
 
8
14
  # Product Manager Toolkit
@@ -2,6 +2,12 @@
2
2
  name: react-doctor
3
3
  description: Run after making React changes to catch issues early. Use when reviewing code, finishing a feature, or fixing bugs in a React project.
4
4
  version: 1.0.0
5
+ metadata:
6
+ author: Million.dev (millionco)
7
+ source: https://github.com/millionco/react-doctor
8
+ adapted-by: "Juan Carlos Cadavid — jcadavid.com"
9
+ adapted-for: "AKILI-SPECS"
10
+ binding: stack
5
11
  ---
6
12
 
7
13
  # React Doctor