@cosmictraveler002/anim-kit 1.1.0 → 1.2.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 (38) hide show
  1. package/README.md +114 -29
  2. package/dist/anim-kit.standalone.js +18 -8
  3. package/dist/anim-kit.standalone.js.map +1 -1
  4. package/dist/core/gsap.d.ts +2 -1
  5. package/dist/core/gsap.d.ts.map +1 -1
  6. package/dist/core/gsap.js +3 -2
  7. package/dist/core/gsap.js.map +1 -1
  8. package/dist/effects/clip-wipe.d.ts +10 -2
  9. package/dist/effects/clip-wipe.d.ts.map +1 -1
  10. package/dist/effects/clip-wipe.js +30 -9
  11. package/dist/effects/clip-wipe.js.map +1 -1
  12. package/dist/effects/cursor-follower.d.ts.map +1 -1
  13. package/dist/effects/cursor-follower.js +22 -4
  14. package/dist/effects/cursor-follower.js.map +1 -1
  15. package/dist/effects/flip-words.d.ts +31 -0
  16. package/dist/effects/flip-words.d.ts.map +1 -0
  17. package/dist/effects/flip-words.js +104 -0
  18. package/dist/effects/flip-words.js.map +1 -0
  19. package/dist/effects/magnetic.d.ts +15 -0
  20. package/dist/effects/magnetic.d.ts.map +1 -0
  21. package/dist/effects/magnetic.js +59 -0
  22. package/dist/effects/magnetic.js.map +1 -0
  23. package/dist/effects/media-settle.d.ts +2 -0
  24. package/dist/effects/media-settle.d.ts.map +1 -1
  25. package/dist/effects/media-settle.js +5 -2
  26. package/dist/effects/media-settle.js.map +1 -1
  27. package/dist/index.d.ts +7 -1
  28. package/dist/index.d.ts.map +1 -1
  29. package/dist/index.js +4 -0
  30. package/dist/index.js.map +1 -1
  31. package/package.json +1 -1
  32. package/src/core/gsap.ts +3 -2
  33. package/src/effects/clip-wipe.ts +50 -10
  34. package/src/effects/cursor-follower.ts +21 -4
  35. package/src/effects/flip-words.ts +148 -0
  36. package/src/effects/magnetic.ts +87 -0
  37. package/src/effects/media-settle.ts +7 -1
  38. package/src/index.ts +9 -1
package/README.md CHANGED
@@ -78,7 +78,7 @@ inside the published tarball):
78
78
 
79
79
  | Specifier | Resolves to | Use for |
80
80
  |---|---|---|
81
- | `@cosmictraveler002/anim-kit` | `dist/index.js` + `dist/index.d.ts` | the full barrel — 43 exports |
81
+ | `@cosmictraveler002/anim-kit` | `dist/index.js` + `dist/index.d.ts` | the full barrel — 45 exports |
82
82
  | `@cosmictraveler002/anim-kit/effects/<name>` | `dist/effects/<name>.js` + `.d.ts` | one effect in isolation (`marquee`, `lineReveal`, …) |
83
83
  | `@cosmictraveler002/anim-kit/standalone` | `dist/anim-kit.standalone.js` (types → `index.d.ts`) | the self-contained bundle — same API |
84
84
  | `@cosmictraveler002/anim-kit/styles` | `dist/styles/anim-kit.css` | untouched plain CSS |
@@ -97,7 +97,7 @@ resolves declarations through the same map — no `typesVersions` shim needed.
97
97
 
98
98
  No build step on the consumer's end: `dist/` is served as-is from the npm
99
99
  tarball by any npm CDN. Every URL is **version-pinned** — npm versions are
100
- immutable, so `@cosmictraveler002/anim-kit@1.1.0` always resolves to exactly that build, forever
100
+ immutable, so `@cosmictraveler002/anim-kit@1.2.0` always resolves to exactly that build, forever
101
101
  (only a new version creates a new URL; nothing floats unless you ask for a
102
102
  range).
103
103
 
@@ -108,12 +108,12 @@ plugins anim-kit uses) and `lenis` **inlined** — no import map, one URL, works
108
108
  identically on jsDelivr and unpkg:
109
109
 
110
110
  ```html
111
- <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@cosmictraveler002/anim-kit@1.1.0/dist/styles/anim-kit.css" />
111
+ <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@cosmictraveler002/anim-kit@1.2.0/dist/styles/anim-kit.css" />
112
112
 
113
113
  <script type="module">
114
114
  import {
115
115
  smoothScroll, lineReveal, marquee,
116
- } from "https://cdn.jsdelivr.net/npm/@cosmictraveler002/anim-kit@1.1.0/dist/anim-kit.standalone.js";
116
+ } from "https://cdn.jsdelivr.net/npm/@cosmictraveler002/anim-kit@1.2.0/dist/anim-kit.standalone.js";
117
117
 
118
118
  smoothScroll();
119
119
  lineReveal("[data-lines]", { mode: "scroll" });
@@ -121,7 +121,7 @@ identically on jsDelivr and unpkg:
121
121
  </script>
122
122
  ```
123
123
 
124
- unpkg serves the same file: `https://unpkg.com/@cosmictraveler002/anim-kit@1.1.0/dist/anim-kit.standalone.js`
124
+ unpkg serves the same file: `https://unpkg.com/@cosmictraveler002/anim-kit@1.2.0/dist/anim-kit.standalone.js`
125
125
 
126
126
  ### Option 2 — jsDelivr `+esm`
127
127
 
@@ -130,7 +130,7 @@ per version):
130
130
 
131
131
  ```html
132
132
  <script type="module">
133
- import { lineReveal } from "https://cdn.jsdelivr.net/npm/@cosmictraveler002/anim-kit@1.1.0/+esm";
133
+ import { lineReveal } from "https://cdn.jsdelivr.net/npm/@cosmictraveler002/anim-kit@1.2.0/+esm";
134
134
  </script>
135
135
  ```
136
136
 
@@ -142,17 +142,18 @@ locally, with CDN URLs — and the way to share one GSAP between anim-kit and
142
142
  the rest of your page:
143
143
 
144
144
  ```html
145
- <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@cosmictraveler002/anim-kit@1.1.0/dist/styles/anim-kit.css" />
145
+ <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@cosmictraveler002/anim-kit@1.2.0/dist/styles/anim-kit.css" />
146
146
 
147
147
  <script type="importmap">
148
148
  {
149
149
  "imports": {
150
- "@cosmictraveler002/anim-kit": "https://cdn.jsdelivr.net/npm/@cosmictraveler002/anim-kit@1.1.0/dist/index.js",
150
+ "@cosmictraveler002/anim-kit": "https://cdn.jsdelivr.net/npm/@cosmictraveler002/anim-kit@1.2.0/dist/index.js",
151
151
  "gsap": "https://cdn.jsdelivr.net/npm/gsap@3.15.0/index.js",
152
152
  "gsap/ScrollTrigger": "https://cdn.jsdelivr.net/npm/gsap@3.15.0/ScrollTrigger.js",
153
153
  "gsap/SplitText": "https://cdn.jsdelivr.net/npm/gsap@3.15.0/SplitText.js",
154
154
  "gsap/Draggable": "https://cdn.jsdelivr.net/npm/gsap@3.15.0/Draggable.js",
155
155
  "gsap/CustomEase": "https://cdn.jsdelivr.net/npm/gsap@3.15.0/CustomEase.js",
156
+ "gsap/Flip": "https://cdn.jsdelivr.net/npm/gsap@3.15.0/Flip.js",
156
157
  "gsap/ScrollSmoother": "https://cdn.jsdelivr.net/npm/gsap@3.15.0/ScrollSmoother.js",
157
158
  "lenis": "https://cdn.jsdelivr.net/npm/lenis@1.3.26/dist/lenis.mjs"
158
159
  }
@@ -166,7 +167,7 @@ the rest of your page:
166
167
  </script>
167
168
  ```
168
169
 
169
- Swap the host for unpkg (`https://unpkg.com/@cosmictraveler002/anim-kit@1.1.0/dist/index.js`, …) —
170
+ Swap the host for unpkg (`https://unpkg.com/@cosmictraveler002/anim-kit@1.2.0/dist/index.js`, …) —
170
171
  the file layout is identical. GSAP subpaths are listed one by one because
171
172
  import maps match specifiers literally: a trailing-slash prefix map would
172
173
  produce extension-less URLs, which CDNs don't serve. The `gsap`/`lenis` pins
@@ -281,6 +282,7 @@ prompt dock, and backs the `category` / `subcategory` fields on
281
282
  | Text animations | Decode & scramble | `scrambleText` |
282
283
  | Text animations | Rolling text | `rollText` |
283
284
  | Text animations | Counters | `counter` |
285
+ | Text animations | Layout transfers | `flipWords` |
284
286
  | Scroll & media | Pinned galleries | `horizontalScroll`, `stackedCards`, `stackedCardsPinned` |
285
287
  | Scroll & media | Parallax & depth | `parallax` |
286
288
  | Scroll & media | Heroes & media | `heroShrink`, `mediaSettle` |
@@ -290,6 +292,7 @@ prompt dock, and backs the `category` / `subcategory` fields on
290
292
  | Loops & marquees | Equalizers | `audioBars` |
291
293
  | Buttons & links | Liquid fills | `liquidButton` |
292
294
  | Buttons & links | Underlines | `underlineLink` |
295
+ | Buttons & links | Magnetic hover | `magnetic` |
293
296
  | Navigation & overlays | Menus & nav | `navHide`, `menuOverlay` |
294
297
  | Navigation & overlays | Cursors | `cursorFollower` |
295
298
  | Intros & transitions | Preloaders | `preloader` |
@@ -310,7 +313,7 @@ that does not exist.
310
313
  #### `initGSAP()`
311
314
 
312
315
  Registers the plugins anim-kit relies on (`ScrollTrigger`, `SplitText`,
313
- `Draggable`, `CustomEase`, `ScrollSmoother`) and the studio's custom eases.
316
+ `Draggable`, `CustomEase`, `Flip`, `ScrollSmoother`) and the studio's custom eases.
314
317
  Idempotent; called for you by every effect.
315
318
 
316
319
  #### `EASES`
@@ -349,7 +352,7 @@ Central reduced-motion gate. If the user prefers reduced motion and
349
352
  ### Text animations
350
353
 
351
354
  Typography in motion — masked lines, rising masks, per-character scatter,
352
- decode reveals, rolling words, tickers.
355
+ decode reveals, rolling words, tickers, layout transfers.
353
356
 
354
357
  #### `lineReveal(target, options?) => destroy`
355
358
 
@@ -478,6 +481,39 @@ rollText("[data-roll-rev]", { direction: "down" }); // walk rows in reverse
478
481
  blocks itself. `destroy()` unwraps the rows, removes the clone and restores
479
482
  every inline style — markup comes back byte-identical.
480
483
 
484
+ #### `flipWords(target, { to }, options?) => destroy`
485
+
486
+ Words measured in one layout, moved into another and animated from where
487
+ they stood — the FLIP technique: a column of words fans out into a row,
488
+ driven by scroll progress or played once.
489
+
490
+ ```ts
491
+ // Both blocks share one grid cell so the stage never reflows mid-flight:
492
+ flipWords("[data-flip-from]", { to: "[data-flip-to]", scrub: 0.6 });
493
+
494
+ // One-shot on enter (reverses on leave-back), or immediately:
495
+ flipWords("[data-flip-from]", { to: "[data-flip-to]", mode: "scroll" });
496
+ ```
497
+
498
+ | Option | Default | Notes |
499
+ | --- | --- | --- |
500
+ | `to` | — (required) | destination block; every word is moved into it |
501
+ | `words` | source children | `[data-flip-word]` matches, else the source's element children |
502
+ | `duration` | `1.4` | seconds for one word's travel |
503
+ | `ease` | `"power4.inOut"` | GSAP ease |
504
+ | `stagger` | `0.2` | seconds between word starts |
505
+ | `scale` | `0.2` | mid-flight squash each word pops through (`0` disables) |
506
+ | `mode` | `"scroll"` | or `"immediate"` to play at once |
507
+ | `scrub` | unset | number = scrub smoothing seconds, `true` = immediate |
508
+ | `start / end` | `"top 75%"` / `"bottom 45%"` | ScrollTrigger positions |
509
+
510
+ **DOM:** put the source and destination blocks in the same grid cell
511
+ (`grid-area: 1 / 1`, a `min-height` on both keeps the stage stable). The words
512
+ live in the destination from init but render at their source positions until
513
+ the timeline runs, so scrubbing reverses perfectly. `destroy()` kills the
514
+ timeline, puts every word back in its original parent and restores the
515
+ inline transform.
516
+
481
517
  #### `counter(target, options?) => destroy`
482
518
 
483
519
  Tabular number ticker.
@@ -545,23 +581,28 @@ as authored.
545
581
 
546
582
  #### `clipWipe(target, options?) => destroy`
547
583
 
548
- A `clip-path: inset()` reveal: the element is collapsed behind one edge (or
549
- inside a frame margin) and the inset animates to zero so it wipes into view.
584
+ A `clip-path: inset()` reveal: the element is collapsed behind one edge,
585
+ corner (or inside a frame margin) and the inset animates to zero so it wipes
586
+ into view.
550
587
 
551
588
  ```ts
552
589
  clipWipe("[data-clip]", { from: "left" }); // inset(0 100% 0 0) → 0
553
590
  clipWipe("[data-frame]", { from: "frame", inset: 12 }); // opens out of a frame
591
+ clipWipe("[data-corner]", { from: "bottom-right" }); // opens toward the opposite corner
592
+ clipWipe("[data-scrub]", { from: "frame", scrub: 0.5 }); // bound to scroll progress
554
593
  ```
555
594
 
556
595
  | Option | Default | Notes |
557
596
  | --- | --- | --- |
558
- | `from` | `"left"` | `"left"` / `"right"` / `"top"` / `"bottom"` / `"frame"` |
597
+ | `from` | `"left"` | `"left"` / `"right"` / `"top"` / `"bottom"`, a corner (`"top-left"` / `"top-right"` / `"bottom-left"` / `"bottom-right"`), or `"frame"` |
559
598
  | `inset` | `15` | frame margin in % (`from: "frame"`) |
560
599
  | `duration` | `1` | seconds |
561
600
  | `ease` | `"power3.out"` | GSAP ease |
562
601
  | `stagger` | `0.08` | seconds between targets |
563
602
  | `mode` | `"scroll"` | or `"immediate"` to play at once |
564
603
  | `start` | `"top 85%"` | ScrollTrigger start |
604
+ | `end` | `"top 20%"` | ScrollTrigger end (scrub mode) |
605
+ | `scrub` | unset | number = scrub smoothing seconds, `true` = immediate: tie the wipe to scroll progress |
565
606
  | `replay` | `false` | re-wipe when leaving / re-entering |
566
607
 
567
608
  Works on images, video, blocks and text. `destroy()` removes the inline
@@ -660,7 +701,8 @@ heroShrink("[data-hero-media]", { offsetY: "49vh", scale: 0.23, scrub: 1 });
660
701
 
661
702
  Images and video that arrive slightly oversized and ease down to size as the
662
703
  section enters — content lands instead of popping in. Set `scrub` to bind the
663
- settle to scroll progress instead of playing it once.
704
+ settle to scroll progress, or `replay: true` to reverse back to `from` on
705
+ leave-back and replay on every re-enter.
664
706
 
665
707
  ```ts
666
708
  mediaSettle("[data-settle]", { from: 1.15, duration: 1.5 }); // on enter
@@ -677,6 +719,7 @@ mediaSettle("[data-settle-scrub]", { scrub: 0.5, from: 1.2 }); // scroll-bound
677
719
  | `mode` | `"scroll"` | or `"immediate"` to play at once |
678
720
  | `start` / `end` | `"top 75%"` / `"bottom top"` | ScrollTrigger positions |
679
721
  | `scrub` | unset | number = scrub smoothing seconds, `true` = immediate |
722
+ | `replay` | `false` | re-settle when leaving / re-entering (enter mode) |
680
723
 
681
724
  `destroy()` clears `transform` — media returns to its authored scale. Unlike
682
725
  `heroShrink()` (which scrubs media down as it *leaves*), `mediaSettle()` plays
@@ -774,7 +817,7 @@ would rewrite `height` *after* teardown) and clears the inline height.
774
817
 
775
818
  ### Buttons & links
776
819
 
777
- Hover affordances for CTAs and inline links.
820
+ Hover affordances for CTAs and inline links — fills, underlines, magnetic pulls.
778
821
 
779
822
  #### `liquidButton(target, options?) => destroy`
780
823
 
@@ -818,6 +861,29 @@ Pure CSS under the hood — it just adds/removes the `.ak-underline` class whose
818
861
  `::after` sweep is styled by the companion stylesheet, and `destroy()` removes
819
862
  the class again.
820
863
 
864
+ #### `magnetic(target, options?) => destroy`
865
+
866
+ Buttons and links that lean toward the pointer while hovered — following a
867
+ fraction of the pull with a tilt — then spring back to rest with an elastic
868
+ snap on leave.
869
+
870
+ ```ts
871
+ magnetic("[data-magnet]", { strength: 0.5, rotation: 10, scale: 1.04 });
872
+ ```
873
+
874
+ | Option | Default | Notes |
875
+ | --- | --- | --- |
876
+ | `strength` | `0.4` | how far the element follows the pointer — fraction of its own box |
877
+ | `rotation` | `8` | max tilt in degrees at full pull (`0` disables) |
878
+ | `scale` | `1` | scale held while the pointer is over the element (`1` = none) |
879
+ | `duration` | `1.2` | spring-back duration, seconds |
880
+ | `ease` | `"elastic.out(1, 0.35)"` | spring-back ease |
881
+
882
+ **DOM:** pass a list (selector, array, NodeList) — each element gets its own
883
+ listeners and its own pull. Keep CSS transitions off `transform` for magnetic
884
+ elements (GSAP animates transform directly). `destroy()` removes the
885
+ listeners, kills in-flight tweens and restores the inline transform.
886
+
821
887
  ---
822
888
 
823
889
  ### Navigation & overlays
@@ -861,7 +927,12 @@ Curtain uses `EASES.curtain` (`.76,0,.24,1`).
861
927
 
862
928
  #### `cursorFollower(zone, options?) => destroy`
863
929
 
864
- Spring-followed cursor tag, e.g. "▶ Play Showreel" over a video.
930
+ Spring-followed cursor tag, e.g. "▶ Play Showreel" over a video. The tag is
931
+ pinned `position: fixed` and driven in viewport space (`clientX/Y + offset`),
932
+ so it can live anywhere in the DOM — it only reacts while the pointer is over
933
+ the zone. Style it with `opacity: 0` and light text (`color: #fff`): the
934
+ `exclusion` blend inverts the tag against whatever is behind it, which only
935
+ reads with a light source colour.
865
936
 
866
937
  ```ts
867
938
  cursorFollower("[data-showreel]", {
@@ -1061,16 +1132,30 @@ npm run typecheck # tsc --noEmit (what CI runs)
1061
1132
  ```
1062
1133
 
1063
1134
  The demo page wires the whole effect set against one document —
1064
- `demo/index.html` + `demo/demo.js`. (`stackedCardsPinned`, the
1065
- sibling-viewport variant, plus `split()` are exercised by the unit smoke
1066
- instead.)
1135
+ `demo/index.html` + `demo/demo.js` — and every effect in the catalogue has
1136
+ an on-page *copy prompt* chip (the demo smoke enforces the two-way mapping).
1137
+ (`split()` is exercised by the unit smoke instead.)
1138
+
1139
+ Two pages share that wiring:
1140
+
1141
+ - **`demo/index.html`** (default — `npm run demo` → `/demo/`) — import map on
1142
+ local files (`/node_modules/...`, `/dist/index.js`): fast iteration, works
1143
+ offline, and the page `demo-smoke` drives.
1144
+ - **`demo/anim_kit_live.html`** (`/demo/anim_kit_live.html`) — byte-for-byte
1145
+ the same page wired to the **version-pinned CDN** (jsdelivr `@<version>` for
1146
+ the package, `gsap@3.15.0`, `lenis@1.3.26`): the exact path visitors copy
1147
+ from the prompts and docs, so CDN wiring gets the same manual QA as the
1148
+ local build. `demo-smoke` fails if the two files drift apart or the pin
1149
+ stops matching the release version, and the page only loads once that
1150
+ version is published to npm.
1067
1151
 
1068
1152
  ### Copy-prompt API
1069
1153
 
1070
1154
  The demo server doubles as a **prompt server**: every effect has a ready-to-
1071
- paste *"how to implement this with anim-kit"* prompt — markup, import, CDN
1072
- usage (version-pinned, no build step), initialisation call, options table,
1073
- teardown and gotchas:
1155
+ paste *"how to implement this with anim-kit"* prompt — markup, a numbered
1156
+ copy-paste procedure that runs straight from the version-pinned CDN (no
1157
+ build step, plus the npm import for bundlers), initialisation call, options
1158
+ table, teardown and gotchas:
1074
1159
 
1075
1160
  ```bash
1076
1161
  curl http://localhost:4321/api/prompts # { count, categories, prompts: [{ id, title, summary, category, subcategory, text }] }
@@ -1078,7 +1163,7 @@ curl http://localhost:4321/api/prompts/marquee # one prompt, text/plain
1078
1163
  ```
1079
1164
 
1080
1165
  On the page, every labelled section carries a **copy prompt** chip, and the
1081
- floating **⧉ prompts (27)** button at the bottom right opens the full
1166
+ floating **⧉ prompts (29)** button at the bottom right opens the full
1082
1167
  catalogue grouped by [effect category](#effect-categories) — one click copies
1083
1168
  an effect's prompt (the prompt states its category), *copy all* puts the
1084
1169
  entire set on the clipboard. The catalogue lives in `scripts/prompts.mjs`:
@@ -1089,11 +1174,11 @@ and slotting the effect into a subcategory.
1089
1174
  **Unit smoke** (`scripts/smoke.mjs`) runs the built bundle in **jsdom** and
1090
1175
  asserts:
1091
1176
 
1092
- 1. all 43 exports are present;
1177
+ 1. all 45 exports are present;
1093
1178
  2. plugins (`ScrollTrigger`, `SplitText`, `Draggable`, `CustomEase`,
1094
- `ScrollSmoother`) and the 4 custom eases are registered;
1179
+ `Flip`, `ScrollSmoother`) and the 4 custom eases are registered;
1095
1180
  3. every effect no-ops safely on missing targets;
1096
- 4. 22 effects mount on real markup and unmount cleanly;
1181
+ 4. 24 effects mount on real markup and unmount cleanly;
1097
1182
  5. `preloader` ticks in both the positional and options-object call forms;
1098
1183
  6. `lineReveal` actually splits into masked lines (and per-character masks
1099
1184
  with `split: "chars"`) and restores markup on destroy;
@@ -1132,9 +1217,9 @@ anim-kit/
1132
1217
  │ │ ├─ guard.ts reduced-motion gate
1133
1218
  │ │ ├─ util.ts toArray/one/onReady/compose/raf
1134
1219
  │ │ └─ types.ts TargetLike / Destroy / CommonOptions
1135
- │ ├─ effects/ one file per effect (22 files, 26 effect functions)
1220
+ │ ├─ effects/ one file per effect (24 files, 28 effect functions)
1136
1221
  │ ├─ styles/anim-kit.css companion stylesheet
1137
- │ └─ index.ts barrel — 43 exports
1222
+ │ └─ index.ts barrel — 45 exports
1138
1223
  ├─ demo/ visual demo (import map, no bundler)
1139
1224
  ├─ scripts/
1140
1225
  │ ├─ serve.mjs static server + /api/prompts (:4321)