@mehshekari/gsap-motion 3.1.1 → 3.3.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.
@@ -1,119 +1,136 @@
1
- # Routing
2
-
3
- What to load, and what to do when the request is a feeling rather than a
4
- command.
5
-
6
- ## No argument
7
-
8
- Do not guess a target and do not invent an animation. Ask what should move, and
9
- offer the menu below — grouped, so the question is easy to answer.
10
-
11
- > What are we animating?
12
- >
13
- > - **Entrance** — something appearing: a hero, a section, a list, an image
14
- > - **Scroll** — something driven by scroll position: a sequence, a pin, a
15
- > horizontal run
16
- > - **Interaction** — something answering the pointer: a button, a cursor, a
17
- > card
18
- > - **Continuous** — something always moving: a marquee, an orbit, ambient drift
19
- > - **Transition** — moving between two states: a route, a modal, a layout
20
- > - **Loading** — a loader, a progress indicator, a site intro
21
- > - **Existing** — something already animated that feels wrong, costs too much,
22
- > or should go
23
-
24
- ## Classifying a description
25
-
26
- Most requests are a feeling, not a command. Classify, then route.
27
-
28
- | What they say | What they mean | Route to |
29
- |---|---|---|
30
- | "make it cinematic" | slow, weighted, sequenced, one thing at a time | `intro` · [cinematic](../preset/cinematic.md) |
31
- | "make it feel premium" | fewer moving parts, longer eases, no bounce | `tune` · [motion-design](motion-design.md) |
32
- | "make it feel alive" | ambient continuous motion under static content | `ambient` · [marquee](../preset/marquee.md) |
33
- | "make it pop / bolder" | more contrast in timing, not more elements moving | `tune` · [motion-design](motion-design.md) |
34
- | "like Apple" | scroll-scrubbed, pinned, one idea per screen | `scroll` · [scroll-storytelling](../preset/scroll-storytelling.md) |
35
- | "futuristic / technical" | scramble, draw-on, mechanical easing, grid reveals | `text` + `svg` |
36
- | "liquid / organic" | morph, path motion, overlapping soft easing | `svg` · [loader](../preset/loader.md) |
37
- | "something crazy" | layered techniques with one clear concept | see below |
38
- | "smooth image reveal" | clip-path or mask, not opacity | [reveal](../preset/reveal.md) |
39
- | "the hero is boring" | hierarchy problem before a motion problem | `tune`, then `reveal` |
40
- | "it feels janky" | cost or trigger problem | `audit` · [performance](performance.md) |
41
-
42
- ### "Something crazy"
43
-
44
- A fade-and-scale is a failure of nerve; twelve techniques at once is a failure
45
- of taste. Pick **one concept** and let three techniques serve it. The concept is
46
- the thing you can say in a sentence — "the logo writes itself, then the dot that
47
- drew it runs off and writes the word" — and every technique either serves that
48
- sentence or is cut.
49
-
50
- ## Load map
51
-
52
- The project's own rules come first, then the size tiers in SKILL.md, setup step
53
- 3: a one-element change loads [motion-design](motion-design.md) and the
54
- command's own reference only. The table applies from component size up — a
55
- single hover takes [interaction](interaction.md) and nothing else from its row.
56
-
57
- | Request | Add |
58
- |---|---|
59
- | SVG draw / morph / path | [svg](svg.md), [timeline](timeline.md) |
60
- | Scroll sequence, pin, scrub | [scrolltrigger](scrolltrigger.md), [timeline](timeline.md), [performance](performance.md) |
61
- | Horizontal scroll | [scrolltrigger](scrolltrigger.md), [scroll-storytelling](../preset/scroll-storytelling.md) |
62
- | Text reveal, kinetic type | [text](text.md), [timeline](timeline.md) |
63
- | Magnetic, cursor, hover | [interaction](interaction.md), [performance](performance.md) |
64
- | Drag, flick, inertia | [interaction](interaction.md) |
65
- | Layout / shared element / modal | [flip](flip.md) |
66
- | Page or route transition | [flip](flip.md), [page-transition](../preset/page-transition.md), [react-nextjs](react-nextjs.md) or [frameworks](frameworks.md) |
67
- | Loader, splash, intro | [svg](svg.md), [timeline](timeline.md), [accessibility](accessibility.md) |
68
- | Marquee, orbit, ambient | [timeline](timeline.md), [performance](performance.md) |
69
- | Three.js / R3F | [three-r3f](three-r3f.md), [performance](performance.md) |
70
- | Audit an existing animation | [performance](performance.md), [accessibility](accessibility.md), [react-nextjs](react-nextjs.md) or [frameworks](frameworks.md) |
71
- | Retime / re-ease | [motion-design](motion-design.md) only |
72
-
73
- Do not load a domain file "for completeness". Loading `three-r3f.md` for a
74
- button hover is the failure this map exists to prevent.
75
-
76
- ## `audit`
77
-
78
- **Run the checker first.** It is deterministic, it costs a second, and it finds
79
- the mechanical failures before you spend attention on them:
80
-
81
- ```bash
82
- node .claude/skills/gsap-creative-animation/scripts/audit-gsap.mjs <path>
83
- ```
84
-
85
- **When an animation is not visible, ask whether it was ever built.** Before
86
- touching timings or transforms, check that the code which creates it actually
87
- runs. Animation is routinely built behind a condition — an image decode race, a
88
- `ready.then()`, a ScrollTrigger that has not fired, a `matchMedia` branch that
89
- does not match, an early `return` on a missing ref — and a tween that was never
90
- created looks exactly like one that is mis-timed. A typical case: an image wipe
91
- added only once the image decodes inside a short budget. On a first visit the
92
- image is never cached, the branch never runs, and every hypothesis about the
93
- wipe's geometry is wasted effort.
94
-
95
- Then read for what it cannot see. Check in this order — the first two are where
96
- real bugs live:
97
-
98
- 1. **Lifecycle** — is every animation inside a `useGSAP`/`gsap.context` scope?
99
- Are ScrollTriggers killed? Are listeners removed? Does a `matchMedia` get
100
- reverted? See [react-nextjs](react-nextjs.md).
101
- 2. **Cost** — layout-triggering properties, per-frame React state, filters on
102
- large areas, one ScrollTrigger per item where one would do. See
103
- [performance](performance.md).
104
- 3. **Reduced motion** — is there a branch, and does it still communicate?
105
- 4. **Craft** — rhythm, easing on loops, contrast. See [motion-design](motion-design.md).
106
-
107
- Report findings most-severe first, with the file and line. Do not rewrite
108
- unless asked.
109
-
110
- ## `strip`
111
-
112
- Removing motion is a legitimate outcome and often the right one. Cut an
113
- animation when it answers none of hierarchy, causality, continuity, state, or
114
- personality; when it delays content the visitor came for; when it repeats on
115
- every route and stopped being noticed on the second one; or when it exists
116
- because a plugin was available.
117
-
118
- Say what you removed and what it was costing. Do not replace it with a smaller
119
- animation unless the user asks.
1
+ # Routing
2
+
3
+ What to load, and what to do when the request is a feeling rather than a
4
+ command.
5
+
6
+ ## No argument
7
+
8
+ Do not guess a target and do not invent an animation. Ask what should move, and
9
+ offer the menu below — grouped, so the question is easy to answer.
10
+
11
+ > What are we animating?
12
+ >
13
+ > - **Entrance** — something appearing: a hero, a section, a list, an image
14
+ > - **Scroll** — something driven by scroll position: a sequence, a pin, a
15
+ > horizontal run
16
+ > - **Interaction** — something answering the pointer: a button, a cursor, a
17
+ > card
18
+ > - **Continuous** — something always moving: a marquee, an orbit, ambient drift
19
+ > - **Transition** — moving between two states: a route, a modal, a layout
20
+ > - **Loading** — a loader, a progress indicator, a site intro
21
+ > - **Existing** — something already animated that feels wrong, costs too much,
22
+ > or should go
23
+
24
+ ## Classifying a description
25
+
26
+ Most requests are a feeling, not a command. Classify, then route.
27
+
28
+ | What they say | What they mean | Route to |
29
+ |---|---|---|
30
+ | "make it cinematic" | slow, weighted, sequenced, one thing at a time | `intro` · [cinematic](../preset/cinematic.md) |
31
+ | "make it feel premium" | fewer moving parts, longer eases, no bounce | `tune` · [motion-design](motion-design.md) |
32
+ | "make it feel alive" | ambient continuous motion under static content | `ambient` · [marquee](../preset/marquee.md) |
33
+ | "make it pop / bolder" | more contrast in timing, not more elements moving | `tune` · [motion-design](motion-design.md) |
34
+ | "like Apple" | scroll-scrubbed, pinned, one idea per screen | `scroll` · [scroll-storytelling](../preset/scroll-storytelling.md) |
35
+ | "futuristic / technical" | scramble, draw-on, mechanical easing, grid reveals | `text` + `svg` |
36
+ | "liquid / organic" | morph, path motion, overlapping soft easing | `svg` · [loader](../preset/loader.md) |
37
+ | "something crazy" | layered techniques with one clear concept | see below |
38
+ | "smooth image reveal" | clip-path or mask, not opacity | [reveal](../preset/reveal.md) |
39
+ | "the hero is boring" | hierarchy problem before a motion problem | `tune`, then `reveal` |
40
+ | "it feels janky" | cost or trigger problem | `audit` · [performance](performance.md) |
41
+
42
+ ### "Something crazy"
43
+
44
+ A fade-and-scale is a failure of nerve; twelve techniques at once is a failure
45
+ of taste. Pick **one concept** and let three techniques serve it. The concept is
46
+ the thing you can say in a sentence — "the logo writes itself, then the dot that
47
+ drew it runs off and writes the word" — and every technique either serves that
48
+ sentence or is cut.
49
+
50
+ ## The technique ladder
51
+
52
+ Start at the bottom, and each rung up says why the one below is not enough:
53
+
54
+ 1. **A CSS transition** — one property, one state change, no sequencing.
55
+ 2. **One transform tween** — it needs an ease or a duration CSS cannot say.
56
+ 3. **A timeline** — two or more things must be ordered or overlapped.
57
+ 4. **A plugin** — the shape needs it: draw, morph, path, split. Its weight ships
58
+ to every visitor.
59
+ 5. **A scroll takeover** — the scroll position *is* the story. It takes the
60
+ visitor's control of the page, so it needs the strongest reason of all.
61
+
62
+ Say the rung in Analysis: "a transition would not sequence these, so this is a
63
+ timeline". A plugin named without that sentence is one nobody justified.
64
+
65
+ ## Load map
66
+
67
+ The project's own rules come first, then the size tiers in SKILL.md, setup step
68
+ 3: a one-element change loads [motion-design](motion-design.md) and the
69
+ command's own reference only. The table applies from component size up — a
70
+ single hover takes [interaction](interaction.md) and nothing else from its row.
71
+
72
+ | Request | Add |
73
+ |---|---|
74
+ | SVG draw / morph / path | [svg](svg.md), [timeline](timeline.md) |
75
+ | Scroll sequence, pin, scrub | [scrolltrigger](scrolltrigger.md), [timeline](timeline.md), [performance](performance.md) |
76
+ | Horizontal scroll | [scrolltrigger](scrolltrigger.md), [scroll-storytelling](../preset/scroll-storytelling.md) |
77
+ | Text reveal, kinetic type | [text](text.md), [timeline](timeline.md) |
78
+ | Magnetic, cursor, hover | [interaction](interaction.md), [performance](performance.md) |
79
+ | Drag, flick, inertia | [interaction](interaction.md) |
80
+ | Layout / shared element / modal | [flip](flip.md) |
81
+ | Page or route transition | [flip](flip.md), [page-transition](../preset/page-transition.md), this project's adapter |
82
+ | Loader, splash, intro | [svg](svg.md), [timeline](timeline.md), [accessibility](accessibility.md) |
83
+ | Marquee, orbit, ambient | [timeline](timeline.md), [performance](performance.md) |
84
+ | Three.js / R3F | [three](../adapter/three.md) or [r3f](../adapter/r3f.md), [performance](performance.md) |
85
+ | Audit an existing animation | [performance](performance.md), [accessibility](accessibility.md), this project's adapter |
86
+ | Retime / re-ease | [motion-design](motion-design.md) only |
87
+ | Hover, touch or reduced-motion branching | [devices](devices.md) |
88
+ | Fixing what an audit found | [refine](refine.md) |
89
+
90
+ Do not load a domain file "for completeness". Loading `adapter/three.md` for a
91
+ button hover is the failure this map exists to prevent.
92
+
93
+ ## `audit`
94
+
95
+ **Run the checker first.** It is deterministic, it costs a second, and it finds
96
+ the mechanical failures before you spend attention on them:
97
+
98
+ ```bash
99
+ node .claude/skills/gsap-creative-animation/scripts/audit-gsap.mjs <path>
100
+ ```
101
+
102
+ **When an animation is not visible, ask whether it was ever built.** Before
103
+ touching timings or transforms, check that the code which creates it actually
104
+ runs. Animation is routinely built behind a condition — an image decode race, a
105
+ `ready.then()`, a ScrollTrigger that has not fired, a `matchMedia` branch that
106
+ does not match, an early `return` on a missing ref — and a tween that was never
107
+ created looks exactly like one that is mis-timed. A typical case: an image wipe
108
+ added only once the image decodes inside a short budget. On a first visit the
109
+ image is never cached, the branch never runs, and every hypothesis about the
110
+ wipe's geometry is wasted effort.
111
+
112
+ Then read for what it cannot see. Check in this order — the first two are where
113
+ real bugs live:
114
+
115
+ 1. **Lifecycle** — is every animation inside a `useGSAP`/`gsap.context` scope?
116
+ Are ScrollTriggers killed? Are listeners removed? Does a `matchMedia` get
117
+ reverted? See this project's adapter.
118
+ 2. **Cost** — layout-triggering properties, per-frame React state, filters on
119
+ large areas, one ScrollTrigger per item where one would do. See
120
+ [performance](performance.md).
121
+ 3. **Reduced motion** — is there a branch, and does it still communicate?
122
+ 4. **Craft** — rhythm, easing on loops, contrast. See [motion-design](motion-design.md).
123
+
124
+ Report findings most-severe first, with the file and line. Do not rewrite
125
+ unless asked.
126
+
127
+ ## `strip`
128
+
129
+ Removing motion is a legitimate outcome and often the right one. Cut an
130
+ animation when it answers none of hierarchy, causality, continuity, state, or
131
+ personality; when it delays content the visitor came for; when it repeats on
132
+ every route and stopped being noticed on the second one; or when it exists
133
+ because a plugin was available.
134
+
135
+ Say what you removed and what it was costing. Do not replace it with a smaller
136
+ animation unless the user asks.
@@ -176,7 +176,7 @@ useGSAP(() => {
176
176
 
177
177
  After a route change in the App Router, the new page's layout is measured before
178
178
  images settle. `ScrollTrigger.refresh()` on mount is usually needed. See
179
- [react-nextjs.md](react-nextjs.md).
179
+ [react.md](../adapter/react.md).
180
180
 
181
181
  ## Failures
182
182
 
@@ -24,7 +24,7 @@ as JSX.
24
24
 
25
25
  Without them you end up at `querySelectorAll("path")[3]`, which breaks on the
26
26
  next export. Prefix them, and namespace per instance — see
27
- [react-nextjs.md](react-nextjs.md). For a set animated together, one
27
+ [react.md](../adapter/react.md). For a set animated together, one
28
28
  `data-*` attribute beats a run of ids:
29
29
 
30
30
  ```tsx
@@ -4,6 +4,46 @@ A timeline is how choreography stays editable. Five tweens with hand-tuned
4
4
  delays encode the same animation in a form nobody can retime without redoing
5
5
  the arithmetic.
6
6
 
7
+ ## Scene and beats
8
+
9
+ A scene is a set of named beats, and each beat is a label in the code. Writing
10
+ the beats down before the tweens is what stops a timeline becoming a pile of
11
+ numbers nobody can change.
12
+
13
+ Most scenes are four beats or fewer:
14
+
15
+ | Beat | Says |
16
+ |---|---|
17
+ | Entrance | this arrived |
18
+ | Emphasis | this is the one that matters — **at most one per scene** |
19
+ | Pause | read this before the next thing moves |
20
+ | Exit | this is finished, or making room |
21
+
22
+ ```ts
23
+ const tl = gsap.timeline();
24
+
25
+ tl.addLabel("enter")
26
+ .from("[data-heading]", { yPercent: 110, duration: 0.8 })
27
+ .from("[data-lead]", { autoAlpha: 0, y: 18, duration: 0.6 }, "-=0.5")
28
+ .addLabel("emphasis")
29
+ .add(mark()) // a beat of its own, built elsewhere
30
+ .addLabel("rest");
31
+ ```
32
+
33
+ Two rules follow from it:
34
+
35
+ - **A beat that is more than one tween is a function returning its own
36
+ timeline**, added with `.add()`. The scene then reads as its beats, and the
37
+ beat can be retimed, reordered or dropped without touching the rest.
38
+ - **An ambient loop is handed off after the scene, never added inside it.** An
39
+ infinite child makes the parent infinite, so the scene's own `onComplete`
40
+ never runs and anything waiting on it waits forever — the audit reports that
41
+ as `never-completes`. Start the loop from a `.call()` at the end, or from the
42
+ scene's `onComplete`.
43
+
44
+ The numbered beats in an answer's Motion strategy and the labels in its code are
45
+ the same list. If they have drifted apart, the code is not what was designed.
46
+
7
47
  ## Position
8
48
 
9
49
  The third argument places a child on the timeline. This is the whole API.
@@ -103,7 +143,7 @@ onMouseLeave = () => hover.reverse();
103
143
 
104
144
  `reverse()` uses the same easing backwards, so the return matches the arrival
105
145
  without a second definition. In React, anything built outside the `useGSAP`
106
- body must go through `contextSafe` — see [react-nextjs.md](react-nextjs.md).
146
+ body must go through `contextSafe` — see [react.md](../adapter/react.md).
107
147
 
108
148
  ## Holds
109
149
 
@@ -21,10 +21,16 @@ const OPTIONS = {
21
21
  * `.ts` is read without JSX, because there `<T>(x) => x` is a generic and
22
22
  * `<Type>value` a cast; every other extension is read with it. A `.js` file
23
23
  * with JSX is common enough, and JSX never changes how plain JavaScript parses.
24
+ *
25
+ * A single-file component's `<script>` is read without JSX too: it is
26
+ * TypeScript as often as not, and the template where the angle brackets live is
27
+ * blanked out before it reaches here.
24
28
  */
25
29
  const TYPESCRIPT = Parser.extend(tsPlugin());
26
30
  const TYPESCRIPT_JSX = Parser.extend(tsPlugin({ jsx: true }));
27
31
 
32
+ const WITHOUT_JSX = /\.(?:[mc]?ts|vue|svelte|astro)$/;
33
+
28
34
  /**
29
35
  * Parses `source` as the grammar its `path` implies.
30
36
  *
@@ -34,7 +40,7 @@ const TYPESCRIPT_JSX = Parser.extend(tsPlugin({ jsx: true }));
34
40
  */
35
41
  export function parse(source, path) {
36
42
  const comments = [];
37
- const parser = /\.[mc]?ts$/.test(path) ? TYPESCRIPT : TYPESCRIPT_JSX;
43
+ const parser = WITHOUT_JSX.test(path) ? TYPESCRIPT : TYPESCRIPT_JSX;
38
44
  const ast = parser.parse(source, { ...OPTIONS, onComment: comments });
39
45
  return { ast, comments };
40
46
  }