@humanspeak/svelte-motion 0.4.8 → 0.5.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 (49) hide show
  1. package/dist/html/_MotionContainer.svelte +8 -8
  2. package/dist/index.d.ts +17 -11
  3. package/dist/index.js +9 -9
  4. package/dist/utils/attachable.js +14 -9
  5. package/dist/utils/augmentMotionValue.svelte.d.ts +156 -0
  6. package/dist/utils/augmentMotionValue.svelte.js +193 -0
  7. package/dist/utils/booleanSnapshot.svelte.d.ts +37 -0
  8. package/dist/utils/booleanSnapshot.svelte.js +48 -0
  9. package/dist/utils/dom.d.ts +13 -0
  10. package/dist/utils/dom.js +19 -0
  11. package/dist/utils/inView.svelte.d.ts +209 -0
  12. package/dist/utils/inView.svelte.js +323 -0
  13. package/dist/utils/motionTemplate.svelte.d.ts +53 -0
  14. package/dist/utils/motionTemplate.svelte.js +78 -0
  15. package/dist/utils/motionValue.svelte.d.ts +61 -0
  16. package/dist/utils/motionValue.svelte.js +49 -0
  17. package/dist/utils/reducedMotion.svelte.d.ts +43 -0
  18. package/dist/utils/reducedMotion.svelte.js +80 -0
  19. package/dist/utils/reducedMotionConfig.svelte.d.ts +74 -0
  20. package/dist/utils/reducedMotionConfig.svelte.js +144 -0
  21. package/dist/utils/scroll.svelte.d.ts +91 -0
  22. package/dist/utils/scroll.svelte.js +259 -0
  23. package/dist/utils/spring.svelte.d.ts +2 -6
  24. package/dist/utils/spring.svelte.js +6 -70
  25. package/dist/utils/time.svelte.d.ts +47 -0
  26. package/dist/utils/time.svelte.js +128 -0
  27. package/dist/utils/transform.svelte.d.ts +170 -0
  28. package/dist/utils/transform.svelte.js +189 -0
  29. package/dist/utils/velocity.svelte.d.ts +61 -0
  30. package/dist/utils/velocity.svelte.js +132 -0
  31. package/package.json +1 -1
  32. package/dist/utils/inView.d.ts +0 -136
  33. package/dist/utils/inView.js +0 -266
  34. package/dist/utils/motionTemplate.d.ts +0 -21
  35. package/dist/utils/motionTemplate.js +0 -33
  36. package/dist/utils/motionValue.d.ts +0 -6
  37. package/dist/utils/motionValue.js +0 -13
  38. package/dist/utils/reducedMotion.d.ts +0 -20
  39. package/dist/utils/reducedMotion.js +0 -42
  40. package/dist/utils/reducedMotionConfig.d.ts +0 -39
  41. package/dist/utils/reducedMotionConfig.js +0 -92
  42. package/dist/utils/scroll.d.ts +0 -63
  43. package/dist/utils/scroll.js +0 -79
  44. package/dist/utils/time.d.ts +0 -14
  45. package/dist/utils/time.js +0 -68
  46. package/dist/utils/transform.d.ts +0 -74
  47. package/dist/utils/transform.js +0 -211
  48. package/dist/utils/velocity.d.ts +0 -15
  49. package/dist/utils/velocity.js +0 -62
@@ -0,0 +1,61 @@
1
+ import { type MotionValue as MotionDomMotionValue } from 'motion-dom';
2
+ import { type AugmentedMotionValue } from './augmentMotionValue.svelte.js';
3
+ /**
4
+ * The shape returned by {@link useMotionValue}: a real motion-dom
5
+ * `MotionValue<T>` (so it composes with `animate()`, `useTransform`,
6
+ * `useSpring`, etc. and passes `isMotionValue`) plus a Svelte 5 reactive
7
+ * `.current` getter and a Svelte readable store `.subscribe` shim.
8
+ *
9
+ * @template T The value type — typically `number` or `string`.
10
+ * @see {@link AugmentedMotionValue}
11
+ */
12
+ export type MotionValue<T = number> = AugmentedMotionValue<T>;
13
+ /**
14
+ * Re-export of motion-dom's raw `MotionValue` type for cases where the
15
+ * un-augmented shape is needed (e.g. typing follow sources, function
16
+ * dependencies). Most call sites should prefer {@link MotionValue}.
17
+ */
18
+ export type RawMotionValue<T = number> = MotionDomMotionValue<T>;
19
+ /**
20
+ * Creates a tracked, mutable value backed by motion-dom's `MotionValue`.
21
+ *
22
+ * Use `.set(v)` to write the value imperatively, `.get()` to sample it
23
+ * imperatively, and `.current` to read it inside Svelte 5 reactive scopes
24
+ * (templates, `$derived`, `$effect`). The same value also implements the
25
+ * Svelte readable store contract via `.subscribe(run)`, so legacy `$mv`
26
+ * template syntax and `svelte/store`'s `get()` keep working.
27
+ *
28
+ * Returned object is a real motion-dom `MotionValue` — it composes with
29
+ * `useTransform`, `useSpring`, `useVelocity`, the `animate()` driver, and
30
+ * passes `isMotionValue`. Unlike `useSpring`, writes are immediate — there
31
+ * is no follow source and no animation.
32
+ *
33
+ * Lifecycle: must be called during component initialization. Cleanup is
34
+ * registered via `$effect`; motion-dom's internal listeners and animation
35
+ * subscriptions are released when the surrounding component / effect tears
36
+ * down. Call `.destroy()` to clean up early.
37
+ *
38
+ * SSR-safe: motion-dom's `motionValue` runs without DOM access; on the
39
+ * server reads return the initial value and writes still work, with no
40
+ * timers or listeners attached.
41
+ *
42
+ * @template T The value type — typically `number` or `string`.
43
+ * @param initial The starting value.
44
+ * @returns A `MotionValue<T>` augmented with `.current` and `.subscribe`.
45
+ *
46
+ * @example
47
+ * ```svelte
48
+ * <script lang="ts">
49
+ * import { useMotionValue, useTransform } from '@humanspeak/svelte-motion'
50
+ *
51
+ * const x = useMotionValue(0)
52
+ * const opacity = useTransform(x, [0, 200], [0, 1])
53
+ * </script>
54
+ *
55
+ * <input type="range" min="0" max="200" oninput={(e) => x.set(+e.currentTarget.value)} />
56
+ * <div style="opacity: {opacity.current}">x = {x.current}</div>
57
+ * ```
58
+ *
59
+ * @see https://motion.dev/docs/react-motion-value
60
+ */
61
+ export declare const useMotionValue: <T = number>(initial: T) => MotionValue<T>;
@@ -0,0 +1,49 @@
1
+ import { motionValue } from 'motion-dom';
2
+ import { augmentMotionValue } from './augmentMotionValue.svelte.js';
3
+ /**
4
+ * Creates a tracked, mutable value backed by motion-dom's `MotionValue`.
5
+ *
6
+ * Use `.set(v)` to write the value imperatively, `.get()` to sample it
7
+ * imperatively, and `.current` to read it inside Svelte 5 reactive scopes
8
+ * (templates, `$derived`, `$effect`). The same value also implements the
9
+ * Svelte readable store contract via `.subscribe(run)`, so legacy `$mv`
10
+ * template syntax and `svelte/store`'s `get()` keep working.
11
+ *
12
+ * Returned object is a real motion-dom `MotionValue` — it composes with
13
+ * `useTransform`, `useSpring`, `useVelocity`, the `animate()` driver, and
14
+ * passes `isMotionValue`. Unlike `useSpring`, writes are immediate — there
15
+ * is no follow source and no animation.
16
+ *
17
+ * Lifecycle: must be called during component initialization. Cleanup is
18
+ * registered via `$effect`; motion-dom's internal listeners and animation
19
+ * subscriptions are released when the surrounding component / effect tears
20
+ * down. Call `.destroy()` to clean up early.
21
+ *
22
+ * SSR-safe: motion-dom's `motionValue` runs without DOM access; on the
23
+ * server reads return the initial value and writes still work, with no
24
+ * timers or listeners attached.
25
+ *
26
+ * @template T The value type — typically `number` or `string`.
27
+ * @param initial The starting value.
28
+ * @returns A `MotionValue<T>` augmented with `.current` and `.subscribe`.
29
+ *
30
+ * @example
31
+ * ```svelte
32
+ * <script lang="ts">
33
+ * import { useMotionValue, useTransform } from '@humanspeak/svelte-motion'
34
+ *
35
+ * const x = useMotionValue(0)
36
+ * const opacity = useTransform(x, [0, 200], [0, 1])
37
+ * </script>
38
+ *
39
+ * <input type="range" min="0" max="200" oninput={(e) => x.set(+e.currentTarget.value)} />
40
+ * <div style="opacity: {opacity.current}">x = {x.current}</div>
41
+ * ```
42
+ *
43
+ * @see https://motion.dev/docs/react-motion-value
44
+ */
45
+ export const useMotionValue = (initial) => {
46
+ const value = motionValue(initial);
47
+ $effect(() => () => value.destroy());
48
+ return augmentMotionValue(value);
49
+ };
@@ -0,0 +1,43 @@
1
+ import { type BooleanSnapshot } from './booleanSnapshot.svelte.js';
2
+ /**
3
+ * State returned by {@link useReducedMotion}.
4
+ */
5
+ export type ReducedMotionState = BooleanSnapshot;
6
+ /**
7
+ * Returns a `{ current, subscribe }` object that reflects the user's
8
+ * `prefers-reduced-motion` setting. Mirrors framer-motion's
9
+ * `useReducedMotion` adapted for Svelte 5 runes.
10
+ *
11
+ * - `state.current` is reactive — read it in templates / `$derived` /
12
+ * `$effect` and it tracks the underlying `matchMedia` listener.
13
+ * - `state.subscribe(run)` is the Svelte readable store contract:
14
+ * synchronously emits the current value, then re-emits on every change.
15
+ * Kept for compat with downstream hooks that still consume Svelte
16
+ * readables until the Tier 2 wave lands.
17
+ *
18
+ * Diverges from React framer-motion's plain `boolean | null` return for
19
+ * the same reason as `useCycle`: a `$state`-backed value must live on an
20
+ * object so reads inside getters preserve tracking under runes.
21
+ *
22
+ * SSR-safe: returns a static `{ current: false }` when `window` /
23
+ * `matchMedia` is unavailable, including when `matchMedia` throws.
24
+ *
25
+ * The media listener is bound to the surrounding reactive scope via
26
+ * `$effect` — call this from a component `<script>` block (the standard
27
+ * hook contract). On unmount the listener is detached automatically.
28
+ *
29
+ * @returns A `ReducedMotionState` reflecting the OS reduced-motion setting.
30
+ * @see https://motion.dev/docs/react-reduced-motion
31
+ *
32
+ * @example
33
+ * ```svelte
34
+ * <script lang="ts">
35
+ * import { useReducedMotion } from '@humanspeak/svelte-motion'
36
+ *
37
+ * const reduced = useReducedMotion()
38
+ * </script>
39
+ *
40
+ * <div style:transform={reduced.current ? 'none' : 'rotate(45deg)'} />
41
+ * ```
42
+ */
43
+ export declare const useReducedMotion: () => ReducedMotionState;
@@ -0,0 +1,80 @@
1
+ import { createBooleanSnapshot } from './booleanSnapshot.svelte.js';
2
+ const REDUCED_MOTION_QUERY = '(prefers-reduced-motion: reduce)';
3
+ /**
4
+ * Returns a `{ current, subscribe }` object that reflects the user's
5
+ * `prefers-reduced-motion` setting. Mirrors framer-motion's
6
+ * `useReducedMotion` adapted for Svelte 5 runes.
7
+ *
8
+ * - `state.current` is reactive — read it in templates / `$derived` /
9
+ * `$effect` and it tracks the underlying `matchMedia` listener.
10
+ * - `state.subscribe(run)` is the Svelte readable store contract:
11
+ * synchronously emits the current value, then re-emits on every change.
12
+ * Kept for compat with downstream hooks that still consume Svelte
13
+ * readables until the Tier 2 wave lands.
14
+ *
15
+ * Diverges from React framer-motion's plain `boolean | null` return for
16
+ * the same reason as `useCycle`: a `$state`-backed value must live on an
17
+ * object so reads inside getters preserve tracking under runes.
18
+ *
19
+ * SSR-safe: returns a static `{ current: false }` when `window` /
20
+ * `matchMedia` is unavailable, including when `matchMedia` throws.
21
+ *
22
+ * The media listener is bound to the surrounding reactive scope via
23
+ * `$effect` — call this from a component `<script>` block (the standard
24
+ * hook contract). On unmount the listener is detached automatically.
25
+ *
26
+ * @returns A `ReducedMotionState` reflecting the OS reduced-motion setting.
27
+ * @see https://motion.dev/docs/react-reduced-motion
28
+ *
29
+ * @example
30
+ * ```svelte
31
+ * <script lang="ts">
32
+ * import { useReducedMotion } from '@humanspeak/svelte-motion'
33
+ *
34
+ * const reduced = useReducedMotion()
35
+ * </script>
36
+ *
37
+ * <div style:transform={reduced.current ? 'none' : 'rotate(45deg)'} />
38
+ * ```
39
+ */
40
+ export const useReducedMotion = () => {
41
+ if (typeof window === 'undefined' || typeof window.matchMedia !== 'function') {
42
+ return staticState(false);
43
+ }
44
+ let media;
45
+ try {
46
+ media = window.matchMedia(REDUCED_MOTION_QUERY);
47
+ }
48
+ catch {
49
+ return staticState(false);
50
+ }
51
+ const [state, set] = createBooleanSnapshot(media.matches);
52
+ const handler = (event) => set(event.matches);
53
+ $effect(() => {
54
+ // Resync on (re-)mount in case the OS preference changed while
55
+ // the component was torn down between effect runs.
56
+ set(media.matches);
57
+ if (typeof media.addEventListener === 'function') {
58
+ media.addEventListener('change', handler);
59
+ return () => media.removeEventListener('change', handler);
60
+ }
61
+ // Safari < 14 / older WebKit — addListener/removeListener fallback.
62
+ media.addListener(handler);
63
+ return () => media.removeListener(handler);
64
+ });
65
+ return state;
66
+ };
67
+ /**
68
+ * SSR / no-matchMedia fallback. Static value, no listeners; `subscribe`
69
+ * is a one-shot sync emit + no-op unsubscribe so consumers can wire it
70
+ * the same way they do the live state.
71
+ */
72
+ const staticState = (value) => ({
73
+ get current() {
74
+ return value;
75
+ },
76
+ subscribe(run) {
77
+ run(value);
78
+ return () => undefined;
79
+ }
80
+ });
@@ -0,0 +1,74 @@
1
+ import { type ReducedMotionState } from './reducedMotion.svelte.js';
2
+ /**
3
+ * Returns a copy of `keyframes` with transform-related keys
4
+ * (`x`, `y`, `scale`, `rotate`, `skew`, `translate*`, etc.) removed
5
+ * when `reduced` is `true`. Returns `keyframes` unchanged otherwise.
6
+ *
7
+ * The `transition` key is preserved so per-key transitions still flow
8
+ * through to the animation engine; only the visual *targets* are
9
+ * stripped, not the timing config.
10
+ *
11
+ * @template T Keyframes record (or `undefined`).
12
+ * @param keyframes Source keyframes record — may be `undefined`, in
13
+ * which case the same `undefined` is returned regardless of
14
+ * `reduced`.
15
+ * @param reduced When `true`, strip transform keys; when `false`,
16
+ * return `keyframes` unchanged (by reference).
17
+ * @returns The original `keyframes` when `reduced` is `false` (same
18
+ * reference); otherwise a new record with transform keys filtered
19
+ * out and other keys preserved.
20
+ *
21
+ * @example
22
+ * ```ts
23
+ * filterReducedMotionKeyframes(
24
+ * { x: 100, scale: 1.2, opacity: 0.5 },
25
+ * true
26
+ * )
27
+ * // → { opacity: 0.5 }
28
+ *
29
+ * filterReducedMotionKeyframes(
30
+ * { x: 100, opacity: 0.5, transition: { duration: 0.3 } },
31
+ * true
32
+ * )
33
+ * // → { opacity: 0.5, transition: { duration: 0.3 } }
34
+ * ```
35
+ */
36
+ export declare const filterReducedMotionKeyframes: <T extends Record<string, unknown> | undefined>(keyframes: T, reduced: boolean) => T;
37
+ /**
38
+ * Returns a `{ current, subscribe }` object reflecting the resolved
39
+ * reduced-motion policy for the current component subtree.
40
+ *
41
+ * Resolution combines the nearest `<MotionConfig reducedMotion>` ancestor
42
+ * with the OS-level `prefers-reduced-motion` setting:
43
+ *
44
+ * - `'always'` → always `true`
45
+ * - `'never'` (or no `<MotionConfig>` ancestor) → always `false`
46
+ * - `'user'` → mirrors the OS preference, defaulting to `false` in SSR
47
+ *
48
+ * Both reactive read paths fire on **both** sources changing:
49
+ *
50
+ * - `.current` re-evaluates inside any reactive scope that reads it
51
+ * (templates, `$derived`, `$effect`) when either the OS preference *or*
52
+ * a parent `<MotionConfig reducedMotion={...}>` policy reassigns.
53
+ * - `.subscribe(run)` callbacks are driven by both the OS path
54
+ * (sync via `osReduced.subscribe`) and a `$effect` tracking the
55
+ * `motionConfig.reducedMotion` prop. Legacy store consumers see policy
56
+ * changes too — a fix vs. the prior `derived()`-based impl, which only
57
+ * re-fired on OS changes.
58
+ *
59
+ * @returns A `ReducedMotionState` reflecting the merged policy + OS setting.
60
+ * @see https://motion.dev/docs/react-reduced-motion
61
+ *
62
+ * @example
63
+ * ```svelte
64
+ * <script>
65
+ * import { useReducedMotionConfig } from '@humanspeak/svelte-motion'
66
+ * const reduced = useReducedMotionConfig()
67
+ * </script>
68
+ *
69
+ * {#if !reduced.current}
70
+ * <motion.div animate={{ x: 100 }} />
71
+ * {/if}
72
+ * ```
73
+ */
74
+ export declare const useReducedMotionConfig: () => ReducedMotionState;
@@ -0,0 +1,144 @@
1
+ import { getMotionConfig } from '../components/motionConfig.context.js';
2
+ import { createBooleanSnapshot } from './booleanSnapshot.svelte.js';
3
+ import { useReducedMotion } from './reducedMotion.svelte.js';
4
+ /**
5
+ * CSS / motion property keys that move or rotate an element via `transform`.
6
+ * When reduced motion is active these keys are stripped from animate keyframes
7
+ * so the element stays in place while non-transform properties (opacity, color,
8
+ * etc.) continue to animate.
9
+ */
10
+ const TRANSFORM_KEYS = new Set([
11
+ 'x',
12
+ 'y',
13
+ 'z',
14
+ 'translate',
15
+ 'translateX',
16
+ 'translateY',
17
+ 'translateZ',
18
+ 'scale',
19
+ 'scaleX',
20
+ 'scaleY',
21
+ 'scaleZ',
22
+ 'rotate',
23
+ 'rotateX',
24
+ 'rotateY',
25
+ 'rotateZ',
26
+ 'skew',
27
+ 'skewX',
28
+ 'skewY',
29
+ 'transform',
30
+ 'transformPerspective',
31
+ 'perspective'
32
+ ]);
33
+ /**
34
+ * Returns a copy of `keyframes` with transform-related keys
35
+ * (`x`, `y`, `scale`, `rotate`, `skew`, `translate*`, etc.) removed
36
+ * when `reduced` is `true`. Returns `keyframes` unchanged otherwise.
37
+ *
38
+ * The `transition` key is preserved so per-key transitions still flow
39
+ * through to the animation engine; only the visual *targets* are
40
+ * stripped, not the timing config.
41
+ *
42
+ * @template T Keyframes record (or `undefined`).
43
+ * @param keyframes Source keyframes record — may be `undefined`, in
44
+ * which case the same `undefined` is returned regardless of
45
+ * `reduced`.
46
+ * @param reduced When `true`, strip transform keys; when `false`,
47
+ * return `keyframes` unchanged (by reference).
48
+ * @returns The original `keyframes` when `reduced` is `false` (same
49
+ * reference); otherwise a new record with transform keys filtered
50
+ * out and other keys preserved.
51
+ *
52
+ * @example
53
+ * ```ts
54
+ * filterReducedMotionKeyframes(
55
+ * { x: 100, scale: 1.2, opacity: 0.5 },
56
+ * true
57
+ * )
58
+ * // → { opacity: 0.5 }
59
+ *
60
+ * filterReducedMotionKeyframes(
61
+ * { x: 100, opacity: 0.5, transition: { duration: 0.3 } },
62
+ * true
63
+ * )
64
+ * // → { opacity: 0.5, transition: { duration: 0.3 } }
65
+ * ```
66
+ */
67
+ export const filterReducedMotionKeyframes = (keyframes, reduced) => {
68
+ if (!reduced || !keyframes)
69
+ return keyframes;
70
+ const out = {};
71
+ for (const key of Object.keys(keyframes)) {
72
+ if (!TRANSFORM_KEYS.has(key))
73
+ out[key] = keyframes[key];
74
+ }
75
+ return out;
76
+ };
77
+ /**
78
+ * Returns a `{ current, subscribe }` object reflecting the resolved
79
+ * reduced-motion policy for the current component subtree.
80
+ *
81
+ * Resolution combines the nearest `<MotionConfig reducedMotion>` ancestor
82
+ * with the OS-level `prefers-reduced-motion` setting:
83
+ *
84
+ * - `'always'` → always `true`
85
+ * - `'never'` (or no `<MotionConfig>` ancestor) → always `false`
86
+ * - `'user'` → mirrors the OS preference, defaulting to `false` in SSR
87
+ *
88
+ * Both reactive read paths fire on **both** sources changing:
89
+ *
90
+ * - `.current` re-evaluates inside any reactive scope that reads it
91
+ * (templates, `$derived`, `$effect`) when either the OS preference *or*
92
+ * a parent `<MotionConfig reducedMotion={...}>` policy reassigns.
93
+ * - `.subscribe(run)` callbacks are driven by both the OS path
94
+ * (sync via `osReduced.subscribe`) and a `$effect` tracking the
95
+ * `motionConfig.reducedMotion` prop. Legacy store consumers see policy
96
+ * changes too — a fix vs. the prior `derived()`-based impl, which only
97
+ * re-fired on OS changes.
98
+ *
99
+ * @returns A `ReducedMotionState` reflecting the merged policy + OS setting.
100
+ * @see https://motion.dev/docs/react-reduced-motion
101
+ *
102
+ * @example
103
+ * ```svelte
104
+ * <script>
105
+ * import { useReducedMotionConfig } from '@humanspeak/svelte-motion'
106
+ * const reduced = useReducedMotionConfig()
107
+ * </script>
108
+ *
109
+ * {#if !reduced.current}
110
+ * <motion.div animate={{ x: 100 }} />
111
+ * {/if}
112
+ * ```
113
+ */
114
+ export const useReducedMotionConfig = () => {
115
+ const motionConfig = getMotionConfig();
116
+ const osReduced = useReducedMotion();
117
+ const resolve = () => {
118
+ const policy = motionConfig?.reducedMotion ?? 'never';
119
+ if (policy === 'always')
120
+ return true;
121
+ if (policy === 'never')
122
+ return false;
123
+ return osReduced.current;
124
+ };
125
+ const [state, set] = createBooleanSnapshot(resolve());
126
+ // Sync path: `osReduced.subscribe` fires the run callback on every OS
127
+ // preference change (and once synchronously on subscribe). The
128
+ // snapshot's same-value dedupe absorbs that initial duplicate emit.
129
+ const osUnsub = osReduced.subscribe(() => set(resolve()));
130
+ // Async path: `<MotionConfig reducedMotion>` is exposed via a
131
+ // property getter over the config component's prop, so reading it
132
+ // inside `$effect` tracks reassignments and fires the same `set` —
133
+ // closing the gap the legacy `derived()`-based impl had. Returning
134
+ // `osUnsub` installs it as the effect's cleanup, so the OS
135
+ // subscription is released on unmount.
136
+ $effect(() => {
137
+ // Void the read so the lint unused-expression rule doesn't fire
138
+ // on a deliberate dependency touch.
139
+ void motionConfig?.reducedMotion;
140
+ set(resolve());
141
+ return osUnsub;
142
+ });
143
+ return state;
144
+ };
@@ -0,0 +1,91 @@
1
+ import { type AugmentedMotionValue } from './augmentMotionValue.svelte.js';
2
+ import { type ElementOrGetter } from './dom.js';
3
+ /**
4
+ * A scroll offset edge defined as a string (e.g. `"start"`, `"end"`,
5
+ * `"center"`) or a number (0–1 progress). Each offset entry is a pair of
6
+ * `[target, container]`.
7
+ *
8
+ * Intentionally looser than motion's internal `ScrollOffset` type — that
9
+ * uses template-literal types (`` `${Edge} ${Edge}` ``) we don't want to
10
+ * surface to consumers. The runtime call paths through `scroll(...)` cast
11
+ * `options.offset as never` to bridge the two; safe because every shape our
12
+ * type allows is structurally a member of motion's union.
13
+ */
14
+ type ScrollOffset = Array<[number | string, number | string]> | string[];
15
+ /**
16
+ * Options accepted by {@link useScroll}.
17
+ */
18
+ export type UseScrollOptions = {
19
+ /** Scrollable container to track. Defaults to the page. Accepts an element or a getter function. */
20
+ container?: ElementOrGetter;
21
+ /** Target element to track position of within the container. Accepts an element or a getter function. */
22
+ target?: ElementOrGetter;
23
+ /** Scroll offset configuration for element position tracking. */
24
+ offset?: ScrollOffset;
25
+ /** Which axis to use for the single-axis `progress` value supplied to `scroll()`. */
26
+ axis?: 'x' | 'y';
27
+ };
28
+ /**
29
+ * Return type of {@link useScroll} — four motion-dom `MotionValue<number>`s
30
+ * representing scroll position and normalised progress for both axes, each
31
+ * augmented with `.current` + `.subscribe`.
32
+ */
33
+ export type UseScrollReturn = {
34
+ scrollX: AugmentedMotionValue<number>;
35
+ scrollY: AugmentedMotionValue<number>;
36
+ scrollXProgress: AugmentedMotionValue<number>;
37
+ scrollYProgress: AugmentedMotionValue<number>;
38
+ };
39
+ /**
40
+ * Creates scroll-linked motion values for building scroll-driven animations
41
+ * such as progress indicators and parallax effects.
42
+ *
43
+ * Returns four `MotionValue<number>`s: `scrollX` / `scrollY` (current
44
+ * position in px) and `scrollXProgress` / `scrollYProgress` (0–1
45
+ * normalised). Each is a real motion-dom `MotionValue` augmented with a
46
+ * `$state`-backed `.current` getter and a `.subscribe` shim, so they
47
+ * compose with `useTransform`, `useSpring`, and the rest of the Tier 2
48
+ * surface, and they read reactively in Svelte 5 templates.
49
+ *
50
+ * **GPU-accelerated when supported.** On browsers that implement CSS
51
+ * scroll-timeline (no `target`) or view-timeline (with a `target` and a
52
+ * preset `offset`), the *Progress motion values run on the compositor
53
+ * thread via WAAPI — no per-frame JS callback. The non-progress
54
+ * `scrollX` / `scrollY` motion values always use the JS-driven `scroll()`
55
+ * primitive since the absolute pixel offset isn't directly available from
56
+ * native timelines.
57
+ *
58
+ * `container` and `target` accept either an `HTMLElement` directly or a
59
+ * getter `() => HTMLElement | undefined`. The getter form is the right
60
+ * choice with `bind:this`. Element resolution is deferred to a microtask
61
+ * (matches React framer-motion 1:1, faster than rAF polling), so a getter
62
+ * that hasn't hydrated yet is retried as soon as Svelte's mount tick
63
+ * settles it.
64
+ *
65
+ * Lifecycle: the underlying `scroll()` observer and any accelerate factory
66
+ * attach at mount via `$effect` and detach at unmount, regardless of how
67
+ * many consumers are reading the values. The four motion values are torn
68
+ * down at the same time.
69
+ *
70
+ * SSR-safe: returns four static `motionValue(0)`s with no scroll observer
71
+ * on the server.
72
+ *
73
+ * @param options Optional scroll tracking configuration.
74
+ * @returns Four `MotionValue<number>`s — `scrollX`, `scrollY`, `scrollXProgress`, `scrollYProgress`.
75
+ *
76
+ * @example
77
+ * ```svelte
78
+ * <script>
79
+ * import { useScroll, useSpring } from '@humanspeak/svelte-motion'
80
+ *
81
+ * const { scrollYProgress } = useScroll()
82
+ * const scaleX = useSpring(scrollYProgress)
83
+ * </script>
84
+ *
85
+ * <div style="transform: scaleX({scaleX.current}); transform-origin: left;" />
86
+ * ```
87
+ *
88
+ * @see https://motion.dev/docs/react-use-scroll
89
+ */
90
+ export declare const useScroll: (options?: UseScrollOptions) => UseScrollReturn;
91
+ export {};