@cosmictraveler002/anim-kit 1.0.0 → 1.1.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 (50) hide show
  1. package/README.md +183 -43
  2. package/dist/anim-kit.standalone.js +5 -5
  3. package/dist/anim-kit.standalone.js.map +1 -1
  4. package/dist/core/split.d.ts.map +1 -1
  5. package/dist/core/split.js +15 -2
  6. package/dist/core/split.js.map +1 -1
  7. package/dist/effects/clip-wipe.d.ts +23 -0
  8. package/dist/effects/clip-wipe.d.ts.map +1 -0
  9. package/dist/effects/clip-wipe.js +61 -0
  10. package/dist/effects/clip-wipe.js.map +1 -0
  11. package/dist/effects/counter.d.ts +9 -0
  12. package/dist/effects/counter.d.ts.map +1 -1
  13. package/dist/effects/counter.js +17 -7
  14. package/dist/effects/counter.js.map +1 -1
  15. package/dist/effects/line-reveal.d.ts +6 -2
  16. package/dist/effects/line-reveal.d.ts.map +1 -1
  17. package/dist/effects/line-reveal.js +8 -4
  18. package/dist/effects/line-reveal.js.map +1 -1
  19. package/dist/effects/media-settle.d.ts +28 -0
  20. package/dist/effects/media-settle.d.ts.map +1 -0
  21. package/dist/effects/media-settle.js +52 -0
  22. package/dist/effects/media-settle.js.map +1 -0
  23. package/dist/effects/roll-text.d.ts +13 -0
  24. package/dist/effects/roll-text.d.ts.map +1 -0
  25. package/dist/effects/roll-text.js +91 -0
  26. package/dist/effects/roll-text.js.map +1 -0
  27. package/dist/effects/scramble-text.d.ts +17 -0
  28. package/dist/effects/scramble-text.d.ts.map +1 -0
  29. package/dist/effects/scramble-text.js +93 -0
  30. package/dist/effects/scramble-text.js.map +1 -0
  31. package/dist/effects/unfold-reveal.d.ts +23 -0
  32. package/dist/effects/unfold-reveal.d.ts.map +1 -0
  33. package/dist/effects/unfold-reveal.js +53 -0
  34. package/dist/effects/unfold-reveal.js.map +1 -0
  35. package/dist/index.d.ts +19 -2
  36. package/dist/index.d.ts.map +1 -1
  37. package/dist/index.js +15 -2
  38. package/dist/index.js.map +1 -1
  39. package/dist/styles/anim-kit.css +22 -0
  40. package/package.json +6 -3
  41. package/src/core/split.ts +16 -2
  42. package/src/effects/clip-wipe.ts +101 -0
  43. package/src/effects/counter.ts +26 -6
  44. package/src/effects/line-reveal.ts +12 -5
  45. package/src/effects/media-settle.ts +100 -0
  46. package/src/effects/roll-text.ts +118 -0
  47. package/src/effects/scramble-text.ts +122 -0
  48. package/src/effects/unfold-reveal.ts +92 -0
  49. package/src/index.ts +25 -2
  50. package/src/styles/anim-kit.css +22 -0
package/README.md CHANGED
@@ -9,7 +9,7 @@ no components: every effect resolves plain DOM selectors, so it works with
9
9
  **React, Vue, Next, Svelte, Astro or plain HTML**.
10
10
 
11
11
  ```ts
12
- import { lineReveal, marquee, menuOverlay } from "anim-kit";
12
+ import { lineReveal, marquee, menuOverlay } from "@cosmictraveler002/anim-kit";
13
13
 
14
14
  const destroy = lineReveal("[data-lines]", { mode: "scroll" });
15
15
  // …later (route change, HMR, teardown):
@@ -48,12 +48,12 @@ destroy();
48
48
  ## Install
49
49
 
50
50
  ```bash
51
- npm install anim-kit
51
+ npm install @cosmictraveler002/anim-kit
52
52
  ```
53
53
 
54
54
  ```ts
55
- import { smoothScroll, horizontalScroll } from "anim-kit";
56
- import "anim-kit/styles"; // companion stylesheet (plain .css, optional but recommended)
55
+ import { smoothScroll, horizontalScroll } from "@cosmictraveler002/anim-kit";
56
+ import "@cosmictraveler002/anim-kit/styles"; // companion stylesheet (plain .css, optional but recommended)
57
57
  ```
58
58
 
59
59
  `anim-kit` ships **ESM only** with generated `.d.ts` declarations — no CJS
@@ -78,14 +78,14 @@ inside the published tarball):
78
78
 
79
79
  | Specifier | Resolves to | Use for |
80
80
  |---|---|---|
81
- | `anim-kit` | `dist/index.js` + `dist/index.d.ts` | the full barrel — 38 exports |
82
- | `anim-kit/effects/<name>` | `dist/effects/<name>.js` + `.d.ts` | one effect in isolation (`marquee`, `lineReveal`, …) |
83
- | `anim-kit/standalone` | `dist/anim-kit.standalone.js` (types → `index.d.ts`) | the self-contained bundle — same API |
84
- | `anim-kit/styles` | `dist/styles/anim-kit.css` | untouched plain CSS |
81
+ | `@cosmictraveler002/anim-kit` | `dist/index.js` + `dist/index.d.ts` | the full barrel — 43 exports |
82
+ | `@cosmictraveler002/anim-kit/effects/<name>` | `dist/effects/<name>.js` + `.d.ts` | one effect in isolation (`marquee`, `lineReveal`, …) |
83
+ | `@cosmictraveler002/anim-kit/standalone` | `dist/anim-kit.standalone.js` (types → `index.d.ts`) | the self-contained bundle — same API |
84
+ | `@cosmictraveler002/anim-kit/styles` | `dist/styles/anim-kit.css` | untouched plain CSS |
85
85
 
86
86
  ```ts
87
- import { marquee } from "anim-kit/effects/marquee"; // deep import, no barrel
88
- import "anim-kit/styles";
87
+ import { marquee } from "@cosmictraveler002/anim-kit/effects/marquee"; // deep import, no barrel
88
+ import "@cosmictraveler002/anim-kit/styles";
89
89
  ```
90
90
 
91
91
  TypeScript ≥ 4.7 with `moduleResolution: "bundler"` or `"node16"`/`"nodenext"`
@@ -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 `anim-kit@1.0.0` always resolves to exactly that build, forever
100
+ immutable, so `@cosmictraveler002/anim-kit@1.1.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/anim-kit@1.0.0/dist/styles/anim-kit.css" />
111
+ <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@cosmictraveler002/anim-kit@1.1.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/anim-kit@1.0.0/dist/anim-kit.standalone.js";
116
+ } from "https://cdn.jsdelivr.net/npm/@cosmictraveler002/anim-kit@1.1.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/anim-kit@1.0.0/dist/anim-kit.standalone.js`
124
+ unpkg serves the same file: `https://unpkg.com/@cosmictraveler002/anim-kit@1.1.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/anim-kit@1.0.0/+esm";
133
+ import { lineReveal } from "https://cdn.jsdelivr.net/npm/@cosmictraveler002/anim-kit@1.1.0/+esm";
134
134
  </script>
135
135
  ```
136
136
 
@@ -142,12 +142,12 @@ 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/anim-kit@1.0.0/dist/styles/anim-kit.css" />
145
+ <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@cosmictraveler002/anim-kit@1.1.0/dist/styles/anim-kit.css" />
146
146
 
147
147
  <script type="importmap">
148
148
  {
149
149
  "imports": {
150
- "anim-kit": "https://cdn.jsdelivr.net/npm/anim-kit@1.0.0/dist/index.js",
150
+ "@cosmictraveler002/anim-kit": "https://cdn.jsdelivr.net/npm/@cosmictraveler002/anim-kit@1.1.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",
@@ -160,13 +160,13 @@ the rest of your page:
160
160
  </script>
161
161
 
162
162
  <script type="module">
163
- import { smoothScroll, lineReveal } from "anim-kit";
163
+ import { smoothScroll, lineReveal } from "@cosmictraveler002/anim-kit";
164
164
  // per-effect deep imports work here too:
165
- // import { dragStrip } from "anim-kit/effects/dragStrip";
165
+ // import { dragStrip } from "@cosmictraveler002/anim-kit/effects/dragStrip";
166
166
  </script>
167
167
  ```
168
168
 
169
- Swap the host for unpkg (`https://unpkg.com/anim-kit@1.0.0/dist/index.js`, …) —
169
+ Swap the host for unpkg (`https://unpkg.com/@cosmictraveler002/anim-kit@1.1.0/dist/index.js`, …) —
170
170
  the file layout is identical. GSAP subpaths are listed one by one because
171
171
  import maps match specifiers literally: a trailing-slash prefix map would
172
172
  produce extension-less URLs, which CDNs don't serve. The `gsap`/`lenis` pins
@@ -188,7 +188,7 @@ match `package-lock.json`.
188
188
  ```
189
189
 
190
190
  ```js
191
- import { smoothScroll, lineReveal, marquee, compose } from "anim-kit";
191
+ import { smoothScroll, lineReveal, marquee, compose } from "@cosmictraveler002/anim-kit";
192
192
 
193
193
  const scroller = smoothScroll({ lerp: 0.08, smoothWheel: true });
194
194
 
@@ -278,11 +278,13 @@ prompt dock, and backs the `category` / `subcategory` fields on
278
278
  | Core & setup | Smooth scrolling | `smoothScroll` |
279
279
  | Text animations | Line & mask reveals | `lineReveal`, `maskReveal` |
280
280
  | Text animations | Per-character scatter | `scatterText` |
281
+ | Text animations | Decode & scramble | `scrambleText` |
282
+ | Text animations | Rolling text | `rollText` |
281
283
  | Text animations | Counters | `counter` |
282
284
  | Scroll & media | Pinned galleries | `horizontalScroll`, `stackedCards`, `stackedCardsPinned` |
283
285
  | Scroll & media | Parallax & depth | `parallax` |
284
- | Scroll & media | Heroes & media | `heroShrink` |
285
- | Scroll & media | Enter reveals | `revealRule` |
286
+ | Scroll & media | Heroes & media | `heroShrink`, `mediaSettle` |
287
+ | Scroll & media | Enter reveals | `revealRule`, `unfoldReveal`, `clipWipe` |
286
288
  | Loops & marquees | Marquees | `marquee` |
287
289
  | Loops & marquees | Infinite draggables | `dragStrip` |
288
290
  | Loops & marquees | Equalizers | `audioBars` |
@@ -346,7 +348,8 @@ Central reduced-motion gate. If the user prefers reduced motion and
346
348
 
347
349
  ### Text animations
348
350
 
349
- Typography in motion — masked lines, rising masks, per-character scatter, tickers.
351
+ Typography in motion — masked lines, rising masks, per-character scatter,
352
+ decode reveals, rolling words, tickers.
350
353
 
351
354
  #### `lineReveal(target, options?) => destroy`
352
355
 
@@ -356,12 +359,14 @@ source site.
356
359
  ```ts
357
360
  lineReveal("[data-hero-text]", { mode: "immediate", delay: 0.35 }); // above the fold
358
361
  lineReveal("[data-lines]", { mode: "scroll" }); // reverses on leave
362
+ lineReveal("[data-headline]", { split: "chars", stagger: 0.03 }); // per-character rise
359
363
  ```
360
364
 
361
365
  | Option | Default | Notes |
362
366
  | --------- | --------------- | --------------------------------------- |
363
367
  | `mode` | `"scroll"` | `"scroll"` or `"immediate"` |
364
- | `stagger` | `0.1` | seconds between lines |
368
+ | `split` | `"lines"` | `"lines"` or `"chars"` (per-character masked rise) |
369
+ | `stagger` | `0.1` | seconds between lines (`0.03` for chars) |
365
370
  | `duration`| `1` | seconds |
366
371
  | `ease` | `"power4.out"` | |
367
372
  | `delay` | `0` | seconds |
@@ -369,7 +374,8 @@ lineReveal("[data-lines]", { mode: "scroll" }); // reverses
369
374
  | `end` | `"bottom 10%"` | ScrollTrigger end |
370
375
 
371
376
  **DOM:** any block of text — headings with `<br>` hard breaks work. Produces
372
- `.ak-line-mask > .ak-line` per line; `destroy()` restores the original HTML.
377
+ `.ak-line-mask > .ak-line` per line (or `.ak-char-mask > .ak-char` with
378
+ `split: "chars"`); `destroy()` restores the original HTML.
373
379
 
374
380
  #### `maskReveal(target, options?) => destroy`
375
381
 
@@ -420,6 +426,58 @@ scatterText("[data-scatter-pin]", {
420
426
  `destroy()`). Line measurement waits for `document.fonts.ready` so travel
421
427
  distance is correct with webfonts.
422
428
 
429
+ #### `scrambleText(target, options?) => destroy`
430
+
431
+ The decode / cipher reveal: each character churns through the charset and
432
+ settles on its final glyph, left to right. Letters scramble; digits,
433
+ punctuation and spaces stay put; case is preserved.
434
+
435
+ ```ts
436
+ scrambleText("[data-scramble]"); // once, on enter
437
+ scrambleText("[data-headline]", { mode: "immediate", delay: 0.2 }); // right away
438
+ scrambleText(".nav-link", { mode: "hover", durationPerChar: 0.12 }); // on hover
439
+ ```
440
+
441
+ | Option | Default | Notes |
442
+ | --- | --- | --- |
443
+ | `mode` | `"scroll"` | `"scroll"` (once on enter), `"immediate"`, or `"hover"` (re-scrambles on pointerenter) |
444
+ | `charset` | `"abcdefghijklmnopqrstuvwxyz"` | glyphs letters churn through |
445
+ | `durationPerChar` | `0.18` | seconds each character scrambles |
446
+ | `stagger` | `0.04` | seconds between character starts |
447
+ | `delay` | `0` | seconds before the timeline starts |
448
+ | `start` | `"top 80%"` | ScrollTrigger start (`mode: "scroll"`) |
449
+
450
+ **DOM:** plain-text elements only — the effect rewrites `textContent` while
451
+ scrambling and restores the original exactly on `destroy()`.
452
+
453
+ #### `rollText(target, options?) => destroy`
454
+
455
+ The rolling word rotator: two or more rows stacked into a hidden overflow box
456
+ one row tall, rolling to the next on an interval. The first row is cloned at
457
+ the end so the wrap is seamless (same trick as `marquee()`).
458
+
459
+ ```html
460
+ <span class="ak-roll" data-roll>
461
+ <span>Design</span><span>Build</span><span>Motion</span>
462
+ </span>
463
+ ```
464
+
465
+ ```ts
466
+ rollText("[data-roll]", { interval: 2.2, duration: 0.6 });
467
+ rollText("[data-roll-rev]", { direction: "down" }); // walk rows in reverse
468
+ ```
469
+
470
+ | Option | Default | Notes |
471
+ | --- | --- | --- |
472
+ | `interval` | `2.2` | seconds each row is shown (including the roll) |
473
+ | `duration` | `0.6` | roll duration, seconds |
474
+ | `ease` | `"power4.inOut"` | GSAP ease for the roll |
475
+ | `direction` | `"up"` | `"up"` or `"down"` |
476
+
477
+ **DOM:** rows are direct children of the target; the effect stacks them as
478
+ blocks itself. `destroy()` unwraps the rows, removes the clone and restores
479
+ every inline style — markup comes back byte-identical.
480
+
423
481
  #### `counter(target, options?) => destroy`
424
482
 
425
483
  Tabular number ticker.
@@ -428,6 +486,7 @@ Tabular number ticker.
428
486
  counter("[data-count]", { to: 240, duration: 3, suffix: "+" });
429
487
  counter("[data-count-scroll]", { to: 98, onScroll: true }); // waits for view
430
488
  counter("[data-count-pad]", { to: 42, pad: 3 }); // 000 → 042
489
+ counter("[data-progress]", { progress: true, pad: 2, suffix: "%" }); // scrubs 00% → 100%
431
490
  ```
432
491
 
433
492
  | Option | Default |
@@ -438,7 +497,8 @@ counter("[data-count-pad]", { to: 42, pad: 3 }); // 000 →
438
497
  | `pad` | `0` (none) |
439
498
  | `suffix` | `""` |
440
499
  | `onScroll` | `false` |
441
- | `start` | ScrollTrigger start when `onScroll` |
500
+ | `progress` | `false` — scrub the value from scroll progress instead of a timed tween |
501
+ | `start` / `end` | `"top 90%"` / `"bottom top"` — ScrollTrigger positions (`end` with `progress`) |
442
502
  | `onComplete` | `(value) => {}` |
443
503
 
444
504
  ---
@@ -457,6 +517,56 @@ revealRule("[data-rule]", { duration: 1, delay: 0.2 }); // default ease: EASES.r
457
517
 
458
518
  **DOM:** any element that should animate `width: 0 → 100%` when it enters.
459
519
 
520
+ #### `unfoldReveal(target, options?) => destroy`
521
+
522
+ Blocks that grow open from an edge: `scaleY: 0 → 1` from the top (or bottom)
523
+ for a vertical unfold, `scaleX: 0 → 1` from the left for a horizontal one —
524
+ targets stagger together off the first match's trigger.
525
+
526
+ ```ts
527
+ unfoldReveal("[data-unfold]", { axis: "y", origin: "top" });
528
+ unfoldReveal("[data-unfold-x]", { axis: "x", origin: "left", duration: 1.2 });
529
+ ```
530
+
531
+ | Option | Default | Notes |
532
+ | --- | --- | --- |
533
+ | `axis` | `"y"` | `"y"` → `scaleY`, `"x"` → `scaleX` |
534
+ | `origin` | `"top"` / `"left"` | `transformOrigin`, defaults per axis |
535
+ | `duration` | `0.7` | seconds |
536
+ | `ease` | `"power3.out"` | GSAP ease |
537
+ | `stagger` | `0.08` | seconds between targets |
538
+ | `delay` | `0` | seconds |
539
+ | `mode` | `"scroll"` | or `"immediate"` to play at once |
540
+ | `start` | `"top 85%"` | ScrollTrigger start |
541
+ | `replay` | `false` | re-unfold when leaving / re-entering |
542
+
543
+ `destroy()` clears `transform` + `transform-origin`, so elements rest exactly
544
+ as authored.
545
+
546
+ #### `clipWipe(target, options?) => destroy`
547
+
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.
550
+
551
+ ```ts
552
+ clipWipe("[data-clip]", { from: "left" }); // inset(0 100% 0 0) → 0
553
+ clipWipe("[data-frame]", { from: "frame", inset: 12 }); // opens out of a frame
554
+ ```
555
+
556
+ | Option | Default | Notes |
557
+ | --- | --- | --- |
558
+ | `from` | `"left"` | `"left"` / `"right"` / `"top"` / `"bottom"` / `"frame"` |
559
+ | `inset` | `15` | frame margin in % (`from: "frame"`) |
560
+ | `duration` | `1` | seconds |
561
+ | `ease` | `"power3.out"` | GSAP ease |
562
+ | `stagger` | `0.08` | seconds between targets |
563
+ | `mode` | `"scroll"` | or `"immediate"` to play at once |
564
+ | `start` | `"top 85%"` | ScrollTrigger start |
565
+ | `replay` | `false` | re-wipe when leaving / re-entering |
566
+
567
+ Works on images, video, blocks and text. `destroy()` removes the inline
568
+ `clip-path`, restoring the authored (visible) state.
569
+
460
570
  #### `parallax(target, options?) => destroy`
461
571
 
462
572
  `data-speed` parallax over everything inside `target`.
@@ -546,6 +656,32 @@ heroShrink("[data-hero-media]", { offsetY: "49vh", scale: 0.23, scrub: 1 });
546
656
  // options: offsetX "0px", start "top top", end "bottom top"
547
657
  ```
548
658
 
659
+ #### `mediaSettle(target, options?) => destroy`
660
+
661
+ Images and video that arrive slightly oversized and ease down to size as the
662
+ section enters — content lands instead of popping in. Set `scrub` to bind the
663
+ settle to scroll progress instead of playing it once.
664
+
665
+ ```ts
666
+ mediaSettle("[data-settle]", { from: 1.15, duration: 1.5 }); // on enter
667
+ mediaSettle("[data-settle-scrub]", { scrub: 0.5, from: 1.2 }); // scroll-bound
668
+ ```
669
+
670
+ | Option | Default | Notes |
671
+ | --- | --- | --- |
672
+ | `from` | `1.15` | starting scale — settles down to 1 |
673
+ | `duration` | `1.5` | seconds (enter mode) |
674
+ | `ease` | `"power2.out"` | GSAP ease (enter mode) |
675
+ | `origin` | `"center"` | `transformOrigin` |
676
+ | `stagger` | `0.06` | seconds between targets |
677
+ | `mode` | `"scroll"` | or `"immediate"` to play at once |
678
+ | `start` / `end` | `"top 75%"` / `"bottom top"` | ScrollTrigger positions |
679
+ | `scrub` | unset | number = scrub smoothing seconds, `true` = immediate |
680
+
681
+ `destroy()` clears `transform` — media returns to its authored scale. Unlike
682
+ `heroShrink()` (which scrubs media down as it *leaves*), `mediaSettle()` plays
683
+ the entrance.
684
+
549
685
  ---
550
686
 
551
687
  ### Loops & marquees
@@ -827,14 +963,15 @@ Types: `TargetLike`, `Destroy`, `CommonOptions`.
827
963
  ## Styling
828
964
 
829
965
  ```ts
830
- import "anim-kit/styles"; // → dist/styles/anim-kit.css
966
+ import "@cosmictraveler002/anim-kit/styles"; // → dist/styles/anim-kit.css
831
967
  ```
832
968
 
833
969
  The companion stylesheet supplies:
834
970
 
835
971
  - **Design tokens:** `--ak-primary`, `--ak-curtain`, `--ak-reveal`, `--ak-out`
836
- - **Text masks:** `.ak-line-mask`, `.ak-line`, `.ak-word`, `.ak-space`
972
+ - **Text masks:** `.ak-line-mask`, `.ak-line`, `.ak-char-mask`, `.ak-char`, `.ak-word`, `.ak-space`
837
973
  - **Heading masks:** `.ak-mask`, `.ak-mask__inner`
974
+ - **Rolling text:** `.ak-roll`, `.ak-roll__inner`
838
975
  - **Liquid button:** `.ak-liquid`, `.ak-liquid__wave`, `.ak-liquid__label`
839
976
  - **Underline:** `.ak-underline`
840
977
  - **Marquee:** `.ak-marquee`, `.ak-marquee__viewport`, `.ak-marquee__track`
@@ -931,8 +1068,9 @@ instead.)
931
1068
  ### Copy-prompt API
932
1069
 
933
1070
  The demo server doubles as a **prompt server**: every effect has a ready-to-
934
- paste *"how to implement this with anim-kit"* prompt — markup, import,
935
- initialisation call, options table, teardown and gotchas:
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:
936
1074
 
937
1075
  ```bash
938
1076
  curl http://localhost:4321/api/prompts # { count, categories, prompts: [{ id, title, summary, category, subcategory, text }] }
@@ -940,7 +1078,7 @@ curl http://localhost:4321/api/prompts/marquee # one prompt, text/plain
940
1078
  ```
941
1079
 
942
1080
  On the page, every labelled section carries a **copy prompt** chip, and the
943
- floating **⧉ prompts (22)** button at the bottom right opens the full
1081
+ floating **⧉ prompts (27)** button at the bottom right opens the full
944
1082
  catalogue grouped by [effect category](#effect-categories) — one click copies
945
1083
  an effect's prompt (the prompt states its category), *copy all* puts the
946
1084
  entire set on the clipboard. The catalogue lives in `scripts/prompts.mjs`:
@@ -951,28 +1089,30 @@ and slotting the effect into a subcategory.
951
1089
  **Unit smoke** (`scripts/smoke.mjs`) runs the built bundle in **jsdom** and
952
1090
  asserts:
953
1091
 
954
- 1. all 38 exports are present;
1092
+ 1. all 43 exports are present;
955
1093
  2. plugins (`ScrollTrigger`, `SplitText`, `Draggable`, `CustomEase`,
956
1094
  `ScrollSmoother`) and the 4 custom eases are registered;
957
1095
  3. every effect no-ops safely on missing targets;
958
- 4. 16 effects mount on real markup and unmount cleanly;
1096
+ 4. 22 effects mount on real markup and unmount cleanly;
959
1097
  5. `preloader` ticks in both the positional and options-object call forms;
960
- 6. `lineReveal` actually splits into masked lines and restores markup on
961
- destroy;
962
- 7. `utils`, `compose` and `guard` behave per contract.
1098
+ 6. `lineReveal` actually splits into masked lines (and per-character masks
1099
+ with `split: "chars"`) and restores markup on destroy;
1100
+ 7. `scrambleText` restores its text, `rollText` wraps/unwraps its rows, and
1101
+ `counter({ progress: true })` renders a scrubbed readout;
1102
+ 8. `utils`, `compose` and `guard` behave per contract.
963
1103
 
964
1104
  **Demo smoke** (`scripts/demo-smoke.mjs`) loads the real `demo/index.html` and
965
1105
  executes the real `demo/demo.js` wiring against it, then asserts the effects
966
1106
  actually *did* something (hero split, preloader counter ticking, marquee track
967
1107
  duplicated, per-call liquid directions, menu/theme/smooth-scroll handles in
968
- their initial state), that ~40 ScrollTriggers + a Draggable were created, that
1108
+ their initial state), that ~50 ScrollTriggers + a Draggable were created, that
969
1109
  no console errors were logged, that `dragStrip` tiled its content for the
970
1110
  seamless loop, that every effect referenced on the page has a `/api/prompts`
971
1111
  entry, that the taxonomy classifies every effect exactly once, that the
972
1112
  prompt dock renders one group per category with every effect listed once,
973
1113
  and that teardown leaves **zero** live ScrollTriggers, Draggables or
974
1114
  page-element tweens behind while restoring the original markup (marquee and
975
- drag-strip clones removed).
1115
+ drag-strip clones removed, rolling rows unwrapped, scrambled text restored).
976
1116
 
977
1117
  > jsdom is used deliberately: GSAP's CSSPlugin/Draggable probe element
978
1118
  > style/computed values during registration, which a hand-rolled DOM stub
@@ -992,9 +1132,9 @@ anim-kit/
992
1132
  │ │ ├─ guard.ts reduced-motion gate
993
1133
  │ │ ├─ util.ts toArray/one/onReady/compose/raf
994
1134
  │ │ └─ types.ts TargetLike / Destroy / CommonOptions
995
- │ ├─ effects/ one file per effect (17 files, 21 effect functions)
1135
+ │ ├─ effects/ one file per effect (22 files, 26 effect functions)
996
1136
  │ ├─ styles/anim-kit.css companion stylesheet
997
- │ └─ index.ts barrel — 38 exports
1137
+ │ └─ index.ts barrel — 43 exports
998
1138
  ├─ demo/ visual demo (import map, no bundler)
999
1139
  ├─ scripts/
1000
1140
  │ ├─ serve.mjs static server + /api/prompts (:4321)
@@ -1007,7 +1147,7 @@ anim-kit/
1007
1147
  ├─ LICENSE MIT
1008
1148
  └─ dist/ build output
1009
1149
  ├─ index.js / *.d.ts per-file ESM + declarations (tsc)
1010
- ├─ effects/*.js one module per effect → anim-kit/effects/* subpaths
1150
+ ├─ effects/*.js one module per effect → @cosmictraveler002/anim-kit/effects/* subpaths
1011
1151
  ├─ anim-kit.standalone.js self-contained CDN bundle (gsap+lenis inlined)
1012
1152
  └─ styles/anim-kit.css plain CSS, copied verbatim
1013
1153
  ```
@@ -1019,7 +1159,7 @@ push/PR) and `.github/workflows/release.yml` (tag `v*` → `npm publish
1019
1159
 
1020
1160
  Each effect is an independent module — if you only need the marquee, import
1021
1161
  `marquee` and the bundler drops the rest, or deep-import
1022
- `anim-kit/effects/marquee` to skip the barrel entirely.
1162
+ `@cosmictraveler002/anim-kit/effects/marquee` to skip the barrel entirely.
1023
1163
 
1024
1164
  ---
1025
1165