solid-drift 0.2.0 → 0.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 (54) hide show
  1. package/README.md +933 -10
  2. package/dist/ai.d.ts +207 -0
  3. package/dist/ai.js +570 -0
  4. package/dist/animate.d.ts +2 -2
  5. package/dist/animate.js +2 -2
  6. package/dist/cartoon.d.ts +190 -0
  7. package/dist/cartoon.js +334 -0
  8. package/dist/color.d.ts +53 -0
  9. package/dist/color.js +391 -0
  10. package/dist/directive.d.ts +5 -5
  11. package/dist/directive.js +15 -7
  12. package/dist/easing.d.ts +22 -1
  13. package/dist/easing.js +49 -1
  14. package/dist/flip.d.ts +44 -0
  15. package/dist/flip.js +108 -0
  16. package/dist/horizontal.d.ts +107 -0
  17. package/dist/horizontal.js +208 -0
  18. package/dist/index.d.ts +20 -3
  19. package/dist/index.js +17 -3
  20. package/dist/inview.d.ts +4 -4
  21. package/dist/inview.js +5 -5
  22. package/dist/motion.d.ts +404 -0
  23. package/dist/motion.js +761 -0
  24. package/dist/physics.d.ts +146 -0
  25. package/dist/physics.js +352 -0
  26. package/dist/pointer.d.ts +76 -0
  27. package/dist/pointer.js +123 -0
  28. package/dist/reduced-motion.d.ts +3 -3
  29. package/dist/reduced-motion.js +5 -4
  30. package/dist/scroll.d.ts +3 -3
  31. package/dist/scroll.js +5 -5
  32. package/dist/scrollfx.d.ts +156 -0
  33. package/dist/scrollfx.js +148 -0
  34. package/dist/scrub.d.ts +51 -0
  35. package/dist/scrub.js +67 -0
  36. package/dist/spring.d.ts +39 -4
  37. package/dist/spring.js +26 -7
  38. package/dist/stagger.d.ts +4 -4
  39. package/dist/stagger.js +4 -4
  40. package/dist/text.d.ts +27 -0
  41. package/dist/text.js +84 -0
  42. package/dist/timeline.d.ts +45 -0
  43. package/dist/timeline.js +93 -0
  44. package/dist/trail.d.ts +27 -0
  45. package/dist/trail.js +75 -0
  46. package/dist/tween.d.ts +3 -3
  47. package/dist/tween.js +3 -3
  48. package/dist/typography.d.ts +241 -0
  49. package/dist/typography.js +812 -0
  50. package/dist/velocity.d.ts +44 -0
  51. package/dist/velocity.js +88 -0
  52. package/dist/web3.d.ts +213 -0
  53. package/dist/web3.js +640 -0
  54. package/package.json +1 -1
package/dist/text.d.ts ADDED
@@ -0,0 +1,27 @@
1
+ /**
2
+ * Internal text utilities shared by the motion-graphics primitives.
3
+ *
4
+ * Not part of the public API: family modules import from here, but
5
+ * nothing in this file is re-exported from the package index.
6
+ */
7
+ /** Document owning the element, with an SSR-safe fallback. */
8
+ export declare function ownerDoc(el: Element): Document | undefined;
9
+ /**
10
+ * Split an element's text into per-unit inline-block spans so each
11
+ * letter (or word) can be transformed independently. The original text
12
+ * is preserved as an aria-label for screen readers. Any previous
13
+ * content is replaced.
14
+ */
15
+ export declare function splitUnits(el: Element, unit: "chars" | "words"): HTMLElement[];
16
+ /**
17
+ * Append per-unit inline-block spans for `text` to the element's
18
+ * existing content, without clearing it. Used by streaming text, where
19
+ * each flushed batch adds new units while earlier units keep playing.
20
+ */
21
+ export declare function appendUnits(el: Element, text: string, unit: "chars" | "words"): HTMLElement[];
22
+ /**
23
+ * Deterministic pseudo-random generator (mulberry32). Used for
24
+ * per-unit variance so seeded jitter renders identically on every
25
+ * run, which keeps tests stable and output reproducible.
26
+ */
27
+ export declare function mulberry32(seed: number): () => number;
package/dist/text.js ADDED
@@ -0,0 +1,84 @@
1
+ /**
2
+ * Internal text utilities shared by the motion-graphics primitives.
3
+ *
4
+ * Not part of the public API: family modules import from here, but
5
+ * nothing in this file is re-exported from the package index.
6
+ */
7
+ /** Document owning the element, with an SSR-safe fallback. */
8
+ export function ownerDoc(el) {
9
+ const od = el
10
+ .ownerDocument;
11
+ if (od)
12
+ return od ?? undefined;
13
+ return typeof document !== "undefined" ? document : undefined;
14
+ }
15
+ function pushUnit(doc, parent, content) {
16
+ const s = doc.createElement("span");
17
+ s.textContent = content;
18
+ s.setAttribute("aria-hidden", "true");
19
+ s.style.display = "inline-block";
20
+ s.style.willChange = "transform, opacity, filter";
21
+ parent.appendChild(s);
22
+ return s;
23
+ }
24
+ function appendTokens(doc, parent, text, unit) {
25
+ const spans = [];
26
+ if (unit === "words") {
27
+ for (const word of text.split(/(\s+)/)) {
28
+ if (word.length === 0)
29
+ continue;
30
+ if (/^\s+$/.test(word)) {
31
+ parent.appendChild(doc.createTextNode(word));
32
+ }
33
+ else {
34
+ spans.push(pushUnit(doc, parent, word));
35
+ }
36
+ }
37
+ }
38
+ else {
39
+ for (const ch of text)
40
+ spans.push(pushUnit(doc, parent, ch === " " ? " " : ch));
41
+ }
42
+ return spans;
43
+ }
44
+ /**
45
+ * Split an element's text into per-unit inline-block spans so each
46
+ * letter (or word) can be transformed independently. The original text
47
+ * is preserved as an aria-label for screen readers. Any previous
48
+ * content is replaced.
49
+ */
50
+ export function splitUnits(el, unit) {
51
+ const doc = ownerDoc(el);
52
+ if (!doc)
53
+ return [];
54
+ const text = el.textContent ?? "";
55
+ el.textContent = "";
56
+ el.setAttribute("aria-label", text);
57
+ return appendTokens(doc, el, text, unit);
58
+ }
59
+ /**
60
+ * Append per-unit inline-block spans for `text` to the element's
61
+ * existing content, without clearing it. Used by streaming text, where
62
+ * each flushed batch adds new units while earlier units keep playing.
63
+ */
64
+ export function appendUnits(el, text, unit) {
65
+ const doc = ownerDoc(el);
66
+ if (!doc)
67
+ return [];
68
+ return appendTokens(doc, el, text, unit);
69
+ }
70
+ /**
71
+ * Deterministic pseudo-random generator (mulberry32). Used for
72
+ * per-unit variance so seeded jitter renders identically on every
73
+ * run, which keeps tests stable and output reproducible.
74
+ */
75
+ export function mulberry32(seed) {
76
+ let a = seed >>> 0;
77
+ return () => {
78
+ a |= 0;
79
+ a = (a + 0x6d2b79f5) | 0;
80
+ let t = Math.imul(a ^ (a >>> 15), 1 | a);
81
+ t = (t + Math.imul(t ^ (t >>> 7), 61 | t)) ^ t;
82
+ return ((t ^ (t >>> 14)) >>> 0) / 4294967296;
83
+ };
84
+ }
@@ -0,0 +1,45 @@
1
+ import { type Accessor } from "solid-js";
2
+ import { type AnimateOptions } from "./animate.js";
3
+ export interface TimelineStep extends AnimateOptions {
4
+ /** Start value of this step. */
5
+ from: number;
6
+ /** End value of this step. */
7
+ to: number;
8
+ }
9
+ export type TimelineStatus = "idle" | "running" | "done";
10
+ export interface TimelineControls {
11
+ /** Play every step in order. Resolves when the last step completes. */
12
+ start: () => Promise<void>;
13
+ /** Stop the timeline wherever it is. The start() promise resolves. */
14
+ stop: () => void;
15
+ /** Stop and play again from the first step. */
16
+ replay: () => Promise<void>;
17
+ /** Reactive status: "idle" | "running" | "done". */
18
+ status: Accessor<TimelineStatus>;
19
+ }
20
+ /**
21
+ * Plays a sequence of one-shot animations back to back.
22
+ *
23
+ * Each step is an `animate()` call: `from`/`to` plus duration, delay,
24
+ * easing, and `onUpdate`. Steps run strictly in order, so one step's
25
+ * `onUpdate` can drive one element while the next step drives another,
26
+ * building choreographed entrances without nested callbacks.
27
+ *
28
+ * `stop()` halts mid-step and resolves the in-flight `start()` promise.
29
+ * `replay()` restarts from the first step. The timeline also stops
30
+ * itself on cleanup.
31
+ *
32
+ * Under reduced motion every step jumps straight to its end value (each
33
+ * step's `onUpdate(to)` still runs, so the final state is always
34
+ * correct). SSR-safe: steps apply their end values instantly.
35
+ *
36
+ * ```ts
37
+ * const intro = createTimeline([
38
+ * { from: 0, to: 1, duration: 400, onUpdate: (v) => (title.style.opacity = String(v)) },
39
+ * { from: 24, to: 0, duration: 500, easing: "easeOutExpo", onUpdate: (v) => (title.style.transform = `translateY(${v}px)`) },
40
+ * { from: 0, to: 1, duration: 300, onUpdate: (v) => (cta.style.opacity = String(v)) },
41
+ * ])
42
+ * await intro.start()
43
+ * ```
44
+ */
45
+ export declare function createTimeline(steps: TimelineStep[]): TimelineControls;
@@ -0,0 +1,93 @@
1
+ import { createSignal, onCleanup } from "solid-js";
2
+ import { animate, } from "./animate.js";
3
+ /**
4
+ * Plays a sequence of one-shot animations back to back.
5
+ *
6
+ * Each step is an `animate()` call: `from`/`to` plus duration, delay,
7
+ * easing, and `onUpdate`. Steps run strictly in order, so one step's
8
+ * `onUpdate` can drive one element while the next step drives another,
9
+ * building choreographed entrances without nested callbacks.
10
+ *
11
+ * `stop()` halts mid-step and resolves the in-flight `start()` promise.
12
+ * `replay()` restarts from the first step. The timeline also stops
13
+ * itself on cleanup.
14
+ *
15
+ * Under reduced motion every step jumps straight to its end value (each
16
+ * step's `onUpdate(to)` still runs, so the final state is always
17
+ * correct). SSR-safe: steps apply their end values instantly.
18
+ *
19
+ * ```ts
20
+ * const intro = createTimeline([
21
+ * { from: 0, to: 1, duration: 400, onUpdate: (v) => (title.style.opacity = String(v)) },
22
+ * { from: 24, to: 0, duration: 500, easing: "easeOutExpo", onUpdate: (v) => (title.style.transform = `translateY(${v}px)`) },
23
+ * { from: 0, to: 1, duration: 300, onUpdate: (v) => (cta.style.opacity = String(v)) },
24
+ * ])
25
+ * await intro.start()
26
+ * ```
27
+ */
28
+ export function createTimeline(steps) {
29
+ const [status, setStatus] = createSignal("idle");
30
+ let current = null;
31
+ let runToken = 0;
32
+ const play = async () => {
33
+ const token = ++runToken;
34
+ const alive = () => token === runToken;
35
+ setStatus("running");
36
+ for (const step of steps) {
37
+ if (!alive())
38
+ break;
39
+ const { from, to, ...options } = step;
40
+ if (typeof window === "undefined") {
41
+ options.onUpdate?.(to);
42
+ options.onComplete?.();
43
+ continue;
44
+ }
45
+ await new Promise((resolve) => {
46
+ // Declared before animate() runs: under reduced motion animate()
47
+ // calls onComplete synchronously, before its return value exists.
48
+ // The completed flag keeps `current` from pointing at a control
49
+ // that already finished in that synchronous path.
50
+ let stepControls = null;
51
+ let completed = false;
52
+ stepControls = animate(from, to, {
53
+ ...options,
54
+ onComplete: () => {
55
+ completed = true;
56
+ options.onComplete?.();
57
+ if (current === stepControls)
58
+ current = null;
59
+ resolve();
60
+ },
61
+ });
62
+ if (!completed)
63
+ current = stepControls;
64
+ // `finished` also resolves when the step is stopped, which
65
+ // unblocks the chain, the alive() check then ends the run.
66
+ void stepControls.finished.then(() => {
67
+ if (current === stepControls)
68
+ current = null;
69
+ resolve();
70
+ });
71
+ });
72
+ }
73
+ if (alive())
74
+ setStatus("done");
75
+ };
76
+ const stop = () => {
77
+ runToken++;
78
+ current?.stop();
79
+ current = null;
80
+ setStatus("idle");
81
+ };
82
+ const controls = {
83
+ start: () => play(),
84
+ stop,
85
+ replay: () => {
86
+ stop();
87
+ return play();
88
+ },
89
+ status,
90
+ };
91
+ onCleanup(stop);
92
+ return controls;
93
+ }
@@ -0,0 +1,27 @@
1
+ import { type Accessor } from "solid-js";
2
+ export interface TrailOptions {
3
+ /** How far behind the source the trail follows, in ms. Default 120. */
4
+ delay?: number;
5
+ }
6
+ /**
7
+ * A signal that replays another signal's past: it returns the value the
8
+ * source had `delay` milliseconds ago, interpolated between samples.
9
+ *
10
+ * Chain trails off one source for follower effects (a cursor with a
11
+ * comet tail, cascading highlights), or trail a scroll progress for a
12
+ * delayed echo of the page. The trail catches up and parks exactly on
13
+ * the latest value when the source rests.
14
+ *
15
+ * The follow loop runs on the shared clock only while the trail is
16
+ * behind: it starts when the source changes and stops once caught up.
17
+ *
18
+ * SSR-safe: returns the source itself on the server. Under reduced
19
+ * motion it also returns the source directly (no trailing motion).
20
+ *
21
+ * ```tsx
22
+ * const [tab, setTab] = createSignal(0)
23
+ * // The indicator glides behind the selection instead of jumping.
24
+ * const ghost = createTrail(tab, { delay: 150 })
25
+ * ```
26
+ */
27
+ export declare function createTrail(source: Accessor<number>, options?: TrailOptions): Accessor<number>;
package/dist/trail.js ADDED
@@ -0,0 +1,75 @@
1
+ import { createEffect, createSignal, onCleanup, untrack, } from "solid-js";
2
+ import { now, schedule } from "./engine.js";
3
+ import { prefersReducedMotion } from "./reduced-motion.js";
4
+ /**
5
+ * A signal that replays another signal's past: it returns the value the
6
+ * source had `delay` milliseconds ago, interpolated between samples.
7
+ *
8
+ * Chain trails off one source for follower effects (a cursor with a
9
+ * comet tail, cascading highlights), or trail a scroll progress for a
10
+ * delayed echo of the page. The trail catches up and parks exactly on
11
+ * the latest value when the source rests.
12
+ *
13
+ * The follow loop runs on the shared clock only while the trail is
14
+ * behind: it starts when the source changes and stops once caught up.
15
+ *
16
+ * SSR-safe: returns the source itself on the server. Under reduced
17
+ * motion it also returns the source directly (no trailing motion).
18
+ *
19
+ * ```tsx
20
+ * const [tab, setTab] = createSignal(0)
21
+ * // The indicator glides behind the selection instead of jumping.
22
+ * const ghost = createTrail(tab, { delay: 150 })
23
+ * ```
24
+ */
25
+ export function createTrail(source, options = {}) {
26
+ const { delay = 120 } = options;
27
+ if (typeof window === "undefined" || prefersReducedMotion() || delay <= 0) {
28
+ return source;
29
+ }
30
+ const [value, setValue] = createSignal(untrack(source));
31
+ const samples = [
32
+ [now(), untrack(source)],
33
+ ];
34
+ let cancel = null;
35
+ const tick = (t) => {
36
+ const target = t - delay;
37
+ // Drop samples the trail has already passed (keep one for context).
38
+ while (samples.length > 2 && samples[1][0] <= target)
39
+ samples.shift();
40
+ const [firstT, firstV] = samples[0];
41
+ const [lastT, lastV] = samples[samples.length - 1];
42
+ let current;
43
+ if (samples.length === 1 || target <= firstT) {
44
+ current = firstV;
45
+ }
46
+ else if (target >= lastT) {
47
+ current = lastV;
48
+ }
49
+ else {
50
+ const [ta, va] = samples[0];
51
+ const [tb, vb] = samples[1];
52
+ const p = (target - ta) / Math.max(tb - ta, 0.0001);
53
+ current = va + (vb - va) * p;
54
+ }
55
+ setValue(current);
56
+ if (target >= lastT) {
57
+ cancel = null; // caught up: park exactly on the latest value
58
+ return false;
59
+ }
60
+ return true;
61
+ };
62
+ const kick = () => {
63
+ if (!cancel)
64
+ cancel = schedule(tick);
65
+ };
66
+ createEffect(() => {
67
+ const v = source();
68
+ samples.push([now(), v]);
69
+ if (samples.length > 600)
70
+ samples.splice(0, samples.length - 600);
71
+ kick();
72
+ });
73
+ onCleanup(() => cancel?.());
74
+ return value;
75
+ }
package/dist/tween.d.ts CHANGED
@@ -13,11 +13,11 @@ export interface TweenOptions {
13
13
  /**
14
14
  * A signal that tweens toward a source signal's value over a fixed duration.
15
15
  *
16
- * Interrupting mid-tween retargets from the current value — no snapping.
16
+ * Interrupting mid-tween retargets from the current value, with no snapping.
17
17
  *
18
18
  * ```tsx
19
- * const [open, setOpen] = createSignal(false);
20
- * const opacity = createTween(() => (open() ? 1 : 0), { duration: 250 });
19
+ * const [open, setOpen] = createSignal(false)
20
+ * const opacity = createTween(() => (open() ? 1 : 0), { duration: 250 })
21
21
  * <div style={{ opacity: opacity() }} />
22
22
  * ```
23
23
  */
package/dist/tween.js CHANGED
@@ -5,11 +5,11 @@ import { prefersReducedMotion } from "./reduced-motion.js";
5
5
  /**
6
6
  * A signal that tweens toward a source signal's value over a fixed duration.
7
7
  *
8
- * Interrupting mid-tween retargets from the current value — no snapping.
8
+ * Interrupting mid-tween retargets from the current value, with no snapping.
9
9
  *
10
10
  * ```tsx
11
- * const [open, setOpen] = createSignal(false);
12
- * const opacity = createTween(() => (open() ? 1 : 0), { duration: 250 });
11
+ * const [open, setOpen] = createSignal(false)
12
+ * const opacity = createTween(() => (open() ? 1 : 0), { duration: 250 })
13
13
  * <div style={{ opacity: opacity() }} />
14
14
  * ```
15
15
  */
@@ -0,0 +1,241 @@
1
+ import { type Accessor } from "solid-js";
2
+ import { type Easing, type EasingName } from "./easing.js";
3
+ type MaybeElement = () => Element | null | undefined;
4
+ export interface FontSwapOptions {
5
+ /** Font family to swap to on hover. */
6
+ to: string;
7
+ /** Font weight to swap to. Defaults to keeping the current weight. */
8
+ toWeight?: string | number;
9
+ /** Duration in ms for each half of the letter roll. Default 160. */
10
+ duration?: number;
11
+ /** Stagger in ms between letters. Default 24. */
12
+ stagger?: number;
13
+ /** Easing for the roll. Default "easeInOutCubic". */
14
+ easing?: Easing | EasingName;
15
+ /** On touch devices (no hover) a tap toggles the swap. Default true. */
16
+ tapToToggle?: boolean;
17
+ }
18
+ export interface FontSwapResult {
19
+ /** True while the swapped font is showing. */
20
+ swapped: Accessor<boolean>;
21
+ /** Swap to the alternate font (true) or back (false). */
22
+ swap: (to: boolean) => void;
23
+ /** Toggle between the two fonts. */
24
+ toggle: () => void;
25
+ }
26
+ /**
27
+ * Swap a heading's font family (and optionally weight) on hover with a
28
+ * per-letter roll: each letter flips away in the old font and lands in the
29
+ * new one. Because the letters roll individually, the metric change never
30
+ * reads as a layout jump.
31
+ *
32
+ * On touch devices a tap toggles the swap instead of hover. Under reduced
33
+ * motion the font swaps instantly with no roll.
34
+ */
35
+ export declare function createFontSwap(ref: MaybeElement, options: FontSwapOptions): FontSwapResult;
36
+ export interface TypingOptions {
37
+ /** Text to type. Defaults to the element's current text. */
38
+ text?: string;
39
+ /** Base milliseconds per character. Default 45. */
40
+ speed?: number;
41
+ /** Randomness 0..1 added to each character's timing. Default 0.4. */
42
+ variance?: number;
43
+ /** Extra pause in ms after specific characters. */
44
+ pauses?: Record<string, number>;
45
+ /** Cursor glyph. Set to "" for no cursor. Default "|". */
46
+ cursor?: string;
47
+ /** Cursor blink period in ms. Default 530. */
48
+ blinkRate?: number;
49
+ /** Start typing as soon as the element is available. Default true. */
50
+ autostart?: boolean;
51
+ /** Called when typing completes. */
52
+ onComplete?: () => void;
53
+ }
54
+ export interface TypingResult {
55
+ /** Start typing from the beginning. */
56
+ start: () => void;
57
+ /** Type again from an empty element. */
58
+ replay: () => void;
59
+ /** Stop typing where it is. */
60
+ stop: () => void;
61
+ /** True while characters are being typed. */
62
+ typing: Accessor<boolean>;
63
+ /** True once the full text is showing. */
64
+ completed: Accessor<boolean>;
65
+ }
66
+ /**
67
+ * Type out text character by character with human-like variable speed:
68
+ * each character's delay jitters around the base speed, and punctuation
69
+ * from `pauses` gets a beat of its own. A blinking block cursor rides
70
+ * along and parks itself when the text is done.
71
+ *
72
+ * Under reduced motion (or on the server) the full text appears instantly.
73
+ */
74
+ export declare function createTyping(ref: MaybeElement, options?: TypingOptions): TypingResult;
75
+ export interface TextPhysicsOptions {
76
+ /** How far above their slots (px) letters start falling. Default 320. */
77
+ dropHeight?: number;
78
+ /** Gravity in px/s^2. Default 2600. */
79
+ gravity?: number;
80
+ /** Bounciness 0..1 on each bounce. Default 0.45. */
81
+ bounciness?: number;
82
+ /** Max initial tumble in degrees. Default 200. */
83
+ tumble?: number;
84
+ /** Stagger in ms between letters. Default 45. */
85
+ stagger?: number;
86
+ /** Bounce speed below which a letter settles. Default 60. */
87
+ restThreshold?: number;
88
+ /** Called when every letter has settled. */
89
+ onSettle?: () => void;
90
+ }
91
+ export interface TextPhysicsResult {
92
+ /** Rain the letters in: they fall, tumble, bounce, and settle into place. */
93
+ drop: () => void;
94
+ /** Fling the letters apart so they can rain again. */
95
+ scatter: () => void;
96
+ /** True once every letter has settled. */
97
+ settled: Accessor<boolean>;
98
+ }
99
+ /**
100
+ * Per-letter cartoon physics: `drop()` rains each letter from above with
101
+ * gravity, tumbling rotation, and squashy bounces until every letter lands
102
+ * in its slot. `scatter()` flings the letters apart so they can rain again.
103
+ *
104
+ * Pair it with `createInView` to rain a headline in as it scrolls into view.
105
+ * Under reduced motion (or on the server) letters simply sit in place.
106
+ */
107
+ export declare function createTextPhysics(ref: MaybeElement, options?: TextPhysicsOptions): TextPhysicsResult;
108
+ export interface TextTunnelOptions {
109
+ /** What drives the zoom: "time", "scroll", or your own 0..1 signal. Default "time". */
110
+ drive?: "time" | "scroll" | Accessor<number>;
111
+ /** Seconds per zoom cycle (time drive). Default 3. */
112
+ period?: number;
113
+ /** Number of depth layers. Default 6. */
114
+ layers?: number;
115
+ /** Scale of the front layer. Default 3. */
116
+ zoom?: number;
117
+ }
118
+ export interface TextTunnelResult {
119
+ /** The current 0..1 zoom phase. */
120
+ progress: Accessor<number>;
121
+ /** Stop a time-driven tunnel. */
122
+ stop: () => void;
123
+ }
124
+ /**
125
+ * An infinite 3D text tunnel: copies of the text at staggered depths zoom
126
+ * toward the viewer forever, each fading in from the distance and out past
127
+ * the camera. Drive it with time, with scroll progress, or with your own
128
+ * 0..1 signal.
129
+ *
130
+ * Under reduced motion (or on the server) the text renders as a single
131
+ * static line.
132
+ */
133
+ export declare function createTextTunnel(ref: MaybeElement, options?: TextTunnelOptions): TextTunnelResult;
134
+ export interface TextCutoutOptions {
135
+ /**
136
+ * "gradient": an animated inner world clipped inside the letterforms.
137
+ * "window": transparent fill with a stroked outline, made to overlay a
138
+ * live scene (canvas, video, 3D) so it shows through the letters.
139
+ * Default "gradient".
140
+ */
141
+ mode?: "gradient" | "window";
142
+ /** 0..1 signal driving the inner world. Defaults to a slow time drift. */
143
+ progress?: Accessor<number>;
144
+ /** The three colors of the inner world. */
145
+ palette?: [string, string, string];
146
+ /** Stroke color for "window" mode. Default "currentColor". */
147
+ stroke?: string;
148
+ /** Stroke width in px for "window" mode. Default 1.5. */
149
+ strokeWidth?: number;
150
+ /** Seconds per drift cycle (time drive). Default 9. */
151
+ period?: number;
152
+ }
153
+ /**
154
+ * Turn text into letter-shaped windows onto another world. In "gradient"
155
+ * mode an animated nebula drifts behind the letterforms via
156
+ * background-clip. In "window" mode the fill goes transparent with a
157
+ * stroked outline: overlay it on a canvas, video, or 3D scene and the live
158
+ * scene shows through the letters themselves.
159
+ */
160
+ export declare function createTextCutout(ref: MaybeElement, options?: TextCutoutOptions): {
161
+ stop: () => void;
162
+ };
163
+ export interface TextGradientOptions {
164
+ /** 0..1 signal driving the hue cycle. Defaults to a slow time loop. */
165
+ progress?: Accessor<number>;
166
+ /** Hue degrees spread across the string. Default 140. */
167
+ spread?: number;
168
+ /** Base hue. Default 210. */
169
+ hue?: number;
170
+ /** Saturation percent. Default 85. */
171
+ saturation?: number;
172
+ /** Lightness percent. Default 62. */
173
+ lightness?: number;
174
+ /** Seconds per full hue cycle (time drive). Default 7. */
175
+ period?: number;
176
+ }
177
+ /**
178
+ * Paint each character its own hue and cycle the rainbow across the text:
179
+ * the hue shifts along the string by `spread` degrees and the whole cycle
180
+ * rotates over time (or with your own progress signal, e.g. scroll).
181
+ * Under reduced motion the gradient parks on its first frame.
182
+ */
183
+ export declare function createTextGradient(ref: MaybeElement, options?: TextGradientOptions): {
184
+ stop: () => void;
185
+ };
186
+ export interface TextScrambleOptions {
187
+ /** Text to reveal. Defaults to the element's current text. */
188
+ text?: string;
189
+ /** Glyph pool the letters scramble through. */
190
+ charset?: string;
191
+ /** Stagger in ms between characters starting. Default 28. */
192
+ stagger?: number;
193
+ /** How long in ms each character scrambles before locking in. Default 500. */
194
+ duration?: number;
195
+ /** Milliseconds between glyph swaps. Default 50. */
196
+ frameRate?: number;
197
+ /** Start as soon as the element is available. Default true. */
198
+ autostart?: boolean;
199
+ /** Called when every character has locked in. */
200
+ onComplete?: () => void;
201
+ }
202
+ export interface TextScrambleResult {
203
+ /** Start the decode from the beginning. */
204
+ start: () => void;
205
+ /** Run the decode again. */
206
+ replay: () => void;
207
+ /** Stop mid-decode. */
208
+ stop: () => void;
209
+ /** True while glyphs are still resolving. */
210
+ scrambling: Accessor<boolean>;
211
+ }
212
+ /**
213
+ * A decoder-ring text reveal: every character cycles through random glyphs
214
+ * and locks into its final letter left to right, like a combination lock
215
+ * finding its code. Spaces resolve instantly.
216
+ *
217
+ * Under reduced motion (or on the server) the full text appears instantly.
218
+ */
219
+ export declare function createTextScramble(ref: MaybeElement, options?: TextScrambleOptions): TextScrambleResult;
220
+ export interface TextWaveOptions {
221
+ /** Wave height in px. Default 9. */
222
+ amplitude?: number;
223
+ /** Characters per full wave. Default 7. */
224
+ wavelength?: number;
225
+ /** Seconds per wave cycle. Default 1.8. */
226
+ period?: number;
227
+ /** Tilt each character with the wave slope. Default true. */
228
+ tilt?: boolean;
229
+ /** 0..1 signal driving the phase (e.g. scroll). Defaults to time. */
230
+ progress?: Accessor<number>;
231
+ }
232
+ /**
233
+ * A traveling sine wave across the text: each character bobs up and down
234
+ * (and tilts with the slope) as the wave passes through. Drive it with
235
+ * time for an ambient shimmer or with scroll progress for a wave that
236
+ * moves as you scroll. Under reduced motion the text sits still.
237
+ */
238
+ export declare function createTextWave(ref: MaybeElement, options?: TextWaveOptions): {
239
+ stop: () => void;
240
+ };
241
+ export {};