@humanspeak/svelte-motion 0.1.10 → 0.1.12

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.
@@ -30,6 +30,7 @@
30
30
  getInitialFalseContext
31
31
  } from '../components/variantContext.context'
32
32
  import { writable } from 'svelte/store'
33
+ import { transformSVGPathProperties, computeNormalizedSVGInitialAttrs } from '../utils/svg'
33
34
 
34
35
  type Props = MotionProps & {
35
36
  children?: Snippet
@@ -232,8 +233,22 @@
232
233
  'data-path': dataPath
233
234
  }
234
235
  : {}),
236
+ // Apply normalized SVG path attributes synchronously on first render to avoid flash
237
+ // Compute via svg utils (no dynamic import in SSR/derived expressions)
238
+ ...(() => {
239
+ if (!initialKeyframes) return {}
240
+ const attrs = computeNormalizedSVGInitialAttrs(
241
+ initialKeyframes as Record<string, unknown>
242
+ )
243
+ if (attrs) {
244
+ return attrs
245
+ }
246
+ return {}
247
+ })(),
235
248
  style: mergeInlineStyles(
236
- styleProp,
249
+ initialKeyframes && 'pathLength' in initialKeyframes && isLoaded === 'mounting'
250
+ ? `${styleProp || ''};visibility:hidden`
251
+ : styleProp,
237
252
  initialKeyframes as unknown as Record<string, unknown>,
238
253
  resolvedAnimate as unknown as Record<string, unknown>
239
254
  ),
@@ -243,7 +258,20 @@
243
258
  const runAnimation = () => {
244
259
  if (!element || !resolvedAnimate) return
245
260
  const transitionAnimate: MotionTransition = mergedTransition ?? {}
246
- const payload = $state.snapshot(resolvedAnimate)
261
+ let payload = $state.snapshot(resolvedAnimate)
262
+
263
+ // Transform SVG path properties (pathLength, pathOffset) to their CSS equivalents
264
+ payload = transformSVGPathProperties(
265
+ element,
266
+ payload as Record<string, unknown>
267
+ ) as typeof payload
268
+
269
+ // Ensure dash properties aren't pinned as inline styles
270
+ if (element && (element as HTMLElement).style) {
271
+ ;(element as HTMLElement).style.removeProperty('stroke-dasharray')
272
+ ;(element as HTMLElement).style.removeProperty('stroke-dashoffset')
273
+ }
274
+
247
275
  animateWithLifecycle(
248
276
  element,
249
277
  payload as unknown as DOMKeyframesDefinition,
@@ -408,12 +436,14 @@
408
436
 
409
437
  $effect(() => {
410
438
  if (!(element && isLoaded === 'mounting')) return
439
+
411
440
  if (effectiveAnimate) {
412
441
  // If initial={false}, render at animate state immediately with no transition
413
442
  if (effectiveInitialProp === false && resolvedAnimate) {
414
443
  // Use Motion's animate() with duration:0 so it takes control of these properties
415
444
  // This prevents inline styles from pinning the properties during future animations
416
- const snapshot = $state.snapshot(resolvedAnimate) as Record<string, unknown>
445
+ let snapshot = $state.snapshot(resolvedAnimate) as Record<string, unknown>
446
+ snapshot = transformSVGPathProperties(element!, snapshot)
417
447
  animate(element!, snapshot as DOMKeyframesDefinition, { duration: 0 })
418
448
  // Mark that we've already applied this variant to avoid a second animate pass
419
449
  mountedWithInitialFalse = true
@@ -424,16 +454,50 @@
424
454
  isLoaded = 'ready'
425
455
  } else if (isNotEmpty(initialKeyframes)) {
426
456
  // Apply initial instantly BEFORE exposing 'initial' state
427
- animate(element!, initialKeyframes!, { duration: 0 })
457
+ const transformedInitial = transformSVGPathProperties(
458
+ element!,
459
+ initialKeyframes as Record<string, unknown>
460
+ )
461
+
462
+ // For SVG paths, apply initial state truly synchronously to prevent flash
463
+ const hasSVGProps =
464
+ 'strokeDasharray' in transformedInitial ||
465
+ 'strokeDashoffset' in transformedInitial
466
+ if (hasSVGProps) {
467
+ // Apply presentation attributes to avoid pinning CSS properties
468
+ Object.entries(transformedInitial).forEach(([key, value]) => {
469
+ const v = String(Array.isArray(value) ? value[0] : value)
470
+ if (key === 'strokeDasharray' || key === 'stroke-dasharray') {
471
+ element!.setAttribute('stroke-dasharray', v)
472
+ }
473
+ if (key === 'strokeDashoffset' || key === 'stroke-dashoffset') {
474
+ element!.setAttribute('stroke-dashoffset', v)
475
+ }
476
+ })
477
+ // no-op
478
+ }
479
+ // Avoid pinning: strip stroke dash props from the animate(0) payload
480
+ const initialForAnimate = { ...(transformedInitial as Record<string, unknown>) }
481
+ delete (initialForAnimate as Record<string, unknown>).strokeDasharray
482
+ delete (initialForAnimate as Record<string, unknown>)['stroke-dasharray']
483
+ delete (initialForAnimate as Record<string, unknown>).strokeDashoffset
484
+ delete (initialForAnimate as Record<string, unknown>)['stroke-dashoffset']
485
+
486
+ animate(element!, initialForAnimate as DOMKeyframesDefinition, { duration: 0 })
487
+ // no-op
488
+
428
489
  // Mark initial after styles are applied so tests read CSS=0 while state=initial
490
+ // This also removes visibility:hidden that was hiding SVG paths during mount
429
491
  isLoaded = 'initial'
430
492
  dataPath = 1
493
+
431
494
  // Then promote to ready and run the enter animation
432
495
  requestAnimationFrame(async () => {
433
496
  if (isPlaywright) {
434
497
  await sleep(10)
435
498
  }
436
499
  isLoaded = 'ready'
500
+
437
501
  runAnimation()
438
502
  })
439
503
  } else {
@@ -447,7 +511,8 @@
447
511
  resolvedAnimate
448
512
  ) {
449
513
  // Apply variant styles instantly with duration:0
450
- const snapshot = $state.snapshot(resolvedAnimate) as Record<string, unknown>
514
+ let snapshot = $state.snapshot(resolvedAnimate) as Record<string, unknown>
515
+ snapshot = transformSVGPathProperties(element!, snapshot)
451
516
  animate(element!, snapshot as DOMKeyframesDefinition, { duration: 0 })
452
517
  lastRanVariantKey = currentAnimateKey
453
518
  } else {
@@ -456,7 +521,11 @@
456
521
  }
457
522
  } else if (isNotEmpty(initialKeyframes)) {
458
523
  // Apply initial instantly BEFORE exposing 'initial' state
459
- animate(element!, initialKeyframes!, { duration: 0 })
524
+ const transformedInitial = transformSVGPathProperties(
525
+ element!,
526
+ initialKeyframes as Record<string, unknown>
527
+ )
528
+ animate(element!, transformedInitial as DOMKeyframesDefinition, { duration: 0 })
460
529
  dataPath = 3
461
530
  isLoaded = 'initial'
462
531
  requestAnimationFrame(async () => {
@@ -41,6 +41,7 @@ export const computeFocusBaseline = (el, opts) => {
41
41
  skewY: 0,
42
42
  opacity: 1
43
43
  };
44
+ const cs = getComputedStyle(el);
44
45
  for (const key of Object.keys(whileFocusRecord)) {
45
46
  if (Object.prototype.hasOwnProperty.call(animateRecord, key)) {
46
47
  baseline[key] = animateRecord[key];
@@ -52,9 +53,17 @@ export const computeFocusBaseline = (el, opts) => {
52
53
  baseline[key] = neutralTransformDefaults[key];
53
54
  }
54
55
  else {
55
- const cs = getComputedStyle(el);
56
- if (key in cs) {
57
- baseline[key] = cs[key];
56
+ // Convert camelCase to kebab-case for CSS property access
57
+ const cssProperty = key.replace(/([A-Z])/g, '-$1').toLowerCase();
58
+ const value = cs.getPropertyValue(cssProperty);
59
+ // Always assign a baseline entry to ensure a removal keyframe exists.
60
+ if (value) {
61
+ baseline[key] = value;
62
+ }
63
+ else {
64
+ // Fallback to inline style for that property, else explicit empty string
65
+ const inlineValue = el.style.getPropertyValue(cssProperty);
66
+ baseline[key] = inlineValue || '';
58
67
  }
59
68
  }
60
69
  }
@@ -89,7 +98,15 @@ export const attachWhileFocus = (el, whileFocus, mergedTransition, callbacks, ba
89
98
  };
90
99
  const handleBlur = () => {
91
100
  if (focusBaseline && Object.keys(focusBaseline).length > 0) {
92
- animate(el, focusBaseline, mergedTransition);
101
+ const baselineForAnimation = { ...focusBaseline };
102
+ // For baseline entries that are empty string, proactively clear inline CSS
103
+ for (const [key, v] of Object.entries(baselineForAnimation)) {
104
+ if (v === '') {
105
+ const cssProperty = key.replace(/([A-Z])/g, '-$1').toLowerCase();
106
+ el.style.removeProperty(cssProperty);
107
+ }
108
+ }
109
+ animate(el, baselineForAnimation, mergedTransition);
93
110
  }
94
111
  callbacks?.onEnd?.();
95
112
  };
@@ -94,6 +94,16 @@ export const mergeInlineStyles = (existingStyle, initial, animateFallback) => {
94
94
  case 'cursor':
95
95
  setProp('cursor', value);
96
96
  break;
97
+ // Skip SVG path animation properties - they'll be set by animate()
98
+ case 'pathLength':
99
+ case 'pathOffset':
100
+ case 'pathSpacing':
101
+ case 'strokeDasharray':
102
+ case 'stroke-dasharray':
103
+ case 'strokeDashoffset':
104
+ case 'stroke-dashoffset':
105
+ // Don't add these to inline styles - they interfere with animation
106
+ break;
97
107
  default:
98
108
  // Fallback: write raw as-is for simple CSS props
99
109
  if (typeof value === 'string' || typeof value === 'number') {
@@ -0,0 +1,97 @@
1
+ /**
2
+ * SVG-specific properties that need special handling during animation.
3
+ * These properties are not standard CSS properties and need to be transformed.
4
+ */
5
+ export declare const SVG_PATH_PROPERTIES: Set<string>;
6
+ /**
7
+ * Check if an element is an SVG path element.
8
+ */
9
+ /**
10
+ * Determines whether the provided element is an SVGPathElement.
11
+ *
12
+ * @param {Element} element The candidate element to test.
13
+ * @returns {element is SVGPathElement} True when the element is an SVG path.
14
+ * @example
15
+ * const el = document.createElementNS('http://www.w3.org/2000/svg', 'path')
16
+ * if (isSVGPathElement(el)) {
17
+ * // el is now typed as SVGPathElement
18
+ * }
19
+ */
20
+ export declare const isSVGPathElement: (element: Element) => element is SVGPathElement;
21
+ /**
22
+ * Check if an element is any SVG element.
23
+ */
24
+ /**
25
+ * Determines whether the provided element is an SVGElement.
26
+ *
27
+ * @param {Element} element The candidate element to test.
28
+ * @returns {element is SVGElement} True when the element is an SVG element.
29
+ * @example
30
+ * const svg = document.createElementNS('http://www.w3.org/2000/svg', 'svg')
31
+ * if (isSVGElement(svg)) {
32
+ * // svg is now typed as SVGElement
33
+ * }
34
+ */
35
+ export declare const isSVGElement: (element: Element) => element is SVGElement;
36
+ /**
37
+ * Transform SVG path-specific animation properties to their CSS equivalents.
38
+ *
39
+ * Motion's pathLength property creates a line-drawing effect by manipulating
40
+ * strokeDasharray and strokeDashoffset. This function transforms:
41
+ * - pathLength: 0 -> 1 becomes strokeDasharray: "0 1" -> "1 1"
42
+ * - pathOffset: value becomes strokeDashoffset: -value (inverted for drawing direction)
43
+ *
44
+ * @param element - The SVG element being animated
45
+ * @param keyframes - The animation keyframes that may contain SVG properties
46
+ * @returns Transformed keyframes with CSS-compatible properties
47
+ */
48
+ /**
49
+ * Transforms SVG path-specific animation properties into DOM-compatible attributes.
50
+ *
51
+ * Normalized behavior (React/Framer Motion parity):
52
+ * - Ensures `pathLength="1"` is set when any path prop is present
53
+ * - Maps `pathLength`/`pathSpacing` → `stroke-dasharray` (px)
54
+ * - Maps `pathOffset` → `stroke-dashoffset` (negative px)
55
+ *
56
+ * @param {Element} element The element being animated (must be an SVG path).
57
+ * @param {Record<string, unknown>} keyframes The input keyframes possibly containing path props.
58
+ * @returns {Record<string, unknown>} A transformed keyframe object safe for animation.
59
+ */
60
+ export declare const transformSVGPathProperties: (element: Element, keyframes: Record<string, unknown>) => Record<string, unknown>;
61
+ /**
62
+ * Check if any keyframes contain SVG path properties.
63
+ */
64
+ /**
65
+ * Checks if any SVG path-related properties are present in the keyframes object.
66
+ *
67
+ * @param {Record<string, unknown>} keyframes The keyframes to inspect.
68
+ * @returns {boolean} True if any of `pathLength`, `pathSpacing`, or `pathOffset` are present.
69
+ */
70
+ export declare const hasSVGPathProperties: (keyframes: Record<string, unknown>) => boolean;
71
+ /**
72
+ * Transform initial SVG path properties for initial state setup.
73
+ * This ensures that the initial state also has the proper strokeDasharray values.
74
+ */
75
+ /**
76
+ * Transforms initial keyframes for SVG paths so that the initial state uses
77
+ * normalized dash attributes.
78
+ *
79
+ * @param {Element} element The element being animated (must be an SVG path).
80
+ * @param {Record<string, unknown> | undefined} initial Initial keyframes, if provided.
81
+ * @returns {Record<string, unknown> | undefined} Transformed initial keyframes or the original value.
82
+ */
83
+ export declare const transformInitialSVGPathProperties: (element: Element, initial: Record<string, unknown> | undefined) => Record<string, unknown> | undefined;
84
+ /**
85
+ * Computes normalized SVG path attributes for initial render without requiring an element.
86
+ *
87
+ * Behavior matches React/Framer Motion parity:
88
+ * - Always sets pathLength="1" whenever any of path props are present
89
+ * - stroke-dasharray = px(pathLength) + ' ' + px(pathSpacing ?? 1 - Number(pathLength))
90
+ * - stroke-dashoffset = px(-(pathOffset ?? 0))
91
+ *
92
+ * The returned object is suitable for direct DOM attribute assignment (dash-cased keys).
93
+ *
94
+ * @param {Record<string, unknown> | null | undefined} initial Incoming initial keyframes object
95
+ * @returns {Record<string, string> | null} Normalized attribute map or null if no path props
96
+ */
97
+ export declare const computeNormalizedSVGInitialAttrs: (initial: Record<string, unknown> | null | undefined) => Record<string, string> | null;
@@ -0,0 +1,226 @@
1
+ /**
2
+ * SVG-specific properties that need special handling during animation.
3
+ * These properties are not standard CSS properties and need to be transformed.
4
+ */
5
+ export const SVG_PATH_PROPERTIES = new Set(['pathLength', 'pathOffset', 'pathSpacing']);
6
+ /**
7
+ * Check if an element is an SVG path element.
8
+ */
9
+ /**
10
+ * Determines whether the provided element is an SVGPathElement.
11
+ *
12
+ * @param {Element} element The candidate element to test.
13
+ * @returns {element is SVGPathElement} True when the element is an SVG path.
14
+ * @example
15
+ * const el = document.createElementNS('http://www.w3.org/2000/svg', 'path')
16
+ * if (isSVGPathElement(el)) {
17
+ * // el is now typed as SVGPathElement
18
+ * }
19
+ */
20
+ export const isSVGPathElement = (element) => {
21
+ if (typeof SVGPathElement !== 'undefined') {
22
+ return element instanceof SVGPathElement;
23
+ }
24
+ const nsOk = element.namespaceURI === 'http://www.w3.org/2000/svg';
25
+ const tagOk = element.tagName?.toLowerCase() === 'path';
26
+ return !!(nsOk && tagOk);
27
+ };
28
+ /**
29
+ * Check if an element is any SVG element.
30
+ */
31
+ /**
32
+ * Determines whether the provided element is an SVGElement.
33
+ *
34
+ * @param {Element} element The candidate element to test.
35
+ * @returns {element is SVGElement} True when the element is an SVG element.
36
+ * @example
37
+ * const svg = document.createElementNS('http://www.w3.org/2000/svg', 'svg')
38
+ * if (isSVGElement(svg)) {
39
+ * // svg is now typed as SVGElement
40
+ * }
41
+ */
42
+ export const isSVGElement = (element) => {
43
+ if (typeof SVGElement === 'undefined') {
44
+ return false;
45
+ }
46
+ return element instanceof SVGElement;
47
+ };
48
+ /**
49
+ * Transform SVG path-specific animation properties to their CSS equivalents.
50
+ *
51
+ * Motion's pathLength property creates a line-drawing effect by manipulating
52
+ * strokeDasharray and strokeDashoffset. This function transforms:
53
+ * - pathLength: 0 -> 1 becomes strokeDasharray: "0 1" -> "1 1"
54
+ * - pathOffset: value becomes strokeDashoffset: -value (inverted for drawing direction)
55
+ *
56
+ * @param element - The SVG element being animated
57
+ * @param keyframes - The animation keyframes that may contain SVG properties
58
+ * @returns Transformed keyframes with CSS-compatible properties
59
+ */
60
+ /**
61
+ * Transforms SVG path-specific animation properties into DOM-compatible attributes.
62
+ *
63
+ * Normalized behavior (React/Framer Motion parity):
64
+ * - Ensures `pathLength="1"` is set when any path prop is present
65
+ * - Maps `pathLength`/`pathSpacing` → `stroke-dasharray` (px)
66
+ * - Maps `pathOffset` → `stroke-dashoffset` (negative px)
67
+ *
68
+ * @param {Element} element The element being animated (must be an SVG path).
69
+ * @param {Record<string, unknown>} keyframes The input keyframes possibly containing path props.
70
+ * @returns {Record<string, unknown>} A transformed keyframe object safe for animation.
71
+ */
72
+ export const transformSVGPathProperties = (element, keyframes) => {
73
+ if (!isSVGPathElement(element)) {
74
+ return keyframes;
75
+ }
76
+ // logging removed
77
+ const transformed = { ...keyframes };
78
+ // let hasPathLength = false
79
+ // Transform normalized path props to dash attributes using pathLength="1" semantics
80
+ if ('pathLength' in transformed ||
81
+ 'pathSpacing' in transformed ||
82
+ 'pathOffset' in transformed) {
83
+ try {
84
+ element.setAttribute('pathLength', '1');
85
+ }
86
+ catch {
87
+ void 0;
88
+ }
89
+ const toNum = (v) => typeof v === 'number'
90
+ ? v
91
+ : v != null && /^-?\d+(\.\d+)?(px)?$/i.test(String(v).trim())
92
+ ? parseFloat(String(v))
93
+ : undefined;
94
+ const length = (() => {
95
+ const v = transformed.pathLength;
96
+ const n = toNum(v);
97
+ return n ?? v;
98
+ })();
99
+ const spacing = (() => {
100
+ const v = transformed.pathSpacing;
101
+ const n = toNum(v);
102
+ return n ?? v;
103
+ })();
104
+ const offset = (() => {
105
+ const v = transformed.pathOffset;
106
+ const n = toNum(v);
107
+ return n ?? v;
108
+ })();
109
+ const toPx = (v) => (typeof v === 'number' ? `${v}px` : String(v));
110
+ const buildDashArray = (len, spa) => `${toPx(len)} ${toPx(spa)}`;
111
+ // stroke-dasharray from pathLength/pathSpacing with default spacing = 1 - length
112
+ if (Array.isArray(length)) {
113
+ const lenArr = length;
114
+ const spaArr = Array.isArray(spacing)
115
+ ? spacing
116
+ : lenArr.map((lv) => typeof lv === 'number' ? 1 - lv : spacing);
117
+ const dashArray = lenArr.map((lv, i) => buildDashArray(lv, spaArr[i]));
118
+ transformed.strokeDasharray = dashArray;
119
+ transformed['stroke-dasharray'] = dashArray;
120
+ }
121
+ else if (length !== undefined) {
122
+ const len = length;
123
+ const lenNum = toNum(len) ?? 0;
124
+ const spa = spacing !== undefined ? spacing : 1 - lenNum;
125
+ const dashArray = buildDashArray(len, spa);
126
+ transformed.strokeDasharray = dashArray;
127
+ transformed['stroke-dasharray'] = dashArray;
128
+ }
129
+ // stroke-dashoffset from -pathOffset
130
+ if (Array.isArray(offset)) {
131
+ const offs = offset.map((ov) => {
132
+ const n = toNum(ov);
133
+ return n !== undefined ? `${-n}px` : String(ov);
134
+ });
135
+ transformed.strokeDashoffset = offs;
136
+ transformed['stroke-dashoffset'] = offs;
137
+ }
138
+ else if (offset !== undefined) {
139
+ const n = toNum(offset);
140
+ const off = n !== undefined ? `${-n}px` : String(offset);
141
+ transformed.strokeDashoffset = off;
142
+ transformed['stroke-dashoffset'] = off;
143
+ }
144
+ else {
145
+ // default 0
146
+ ;
147
+ transformed.strokeDashoffset = '0px';
148
+ transformed['stroke-dashoffset'] = '0px';
149
+ }
150
+ delete transformed.pathLength;
151
+ delete transformed.pathSpacing;
152
+ delete transformed.pathOffset;
153
+ }
154
+ // logging removed
155
+ return transformed;
156
+ };
157
+ /**
158
+ * Check if any keyframes contain SVG path properties.
159
+ */
160
+ /**
161
+ * Checks if any SVG path-related properties are present in the keyframes object.
162
+ *
163
+ * @param {Record<string, unknown>} keyframes The keyframes to inspect.
164
+ * @returns {boolean} True if any of `pathLength`, `pathSpacing`, or `pathOffset` are present.
165
+ */
166
+ export const hasSVGPathProperties = (keyframes) => {
167
+ return Object.keys(keyframes).some((key) => SVG_PATH_PROPERTIES.has(key));
168
+ };
169
+ /**
170
+ * Transform initial SVG path properties for initial state setup.
171
+ * This ensures that the initial state also has the proper strokeDasharray values.
172
+ */
173
+ /**
174
+ * Transforms initial keyframes for SVG paths so that the initial state uses
175
+ * normalized dash attributes.
176
+ *
177
+ * @param {Element} element The element being animated (must be an SVG path).
178
+ * @param {Record<string, unknown> | undefined} initial Initial keyframes, if provided.
179
+ * @returns {Record<string, unknown> | undefined} Transformed initial keyframes or the original value.
180
+ */
181
+ export const transformInitialSVGPathProperties = (element, initial) => {
182
+ if (!initial || !isSVGPathElement(element)) {
183
+ return initial;
184
+ }
185
+ // logging removed
186
+ return transformSVGPathProperties(element, initial);
187
+ };
188
+ /**
189
+ * Computes normalized SVG path attributes for initial render without requiring an element.
190
+ *
191
+ * Behavior matches React/Framer Motion parity:
192
+ * - Always sets pathLength="1" whenever any of path props are present
193
+ * - stroke-dasharray = px(pathLength) + ' ' + px(pathSpacing ?? 1 - Number(pathLength))
194
+ * - stroke-dashoffset = px(-(pathOffset ?? 0))
195
+ *
196
+ * The returned object is suitable for direct DOM attribute assignment (dash-cased keys).
197
+ *
198
+ * @param {Record<string, unknown> | null | undefined} initial Incoming initial keyframes object
199
+ * @returns {Record<string, string> | null} Normalized attribute map or null if no path props
200
+ */
201
+ export const computeNormalizedSVGInitialAttrs = (initial) => {
202
+ if (!initial)
203
+ return null;
204
+ const hasAny = 'pathLength' in initial || 'pathSpacing' in initial || 'pathOffset' in initial;
205
+ if (!hasAny)
206
+ return null;
207
+ const toPx = (v) => (typeof v === 'number' ? `${v}px` : String(v));
208
+ const negatePx = (v) => {
209
+ if (typeof v === 'number')
210
+ return `${-v}px`;
211
+ const s = String(v);
212
+ return s.startsWith('-') ? s : /^[\d.]+(px)?$/i.test(s) ? `-${s}` : s;
213
+ };
214
+ const len = initial.pathLength ?? 0;
215
+ const spa = initial.pathSpacing ??
216
+ (typeof len === 'number' ? 1 - len : 1);
217
+ const off = initial.pathOffset ?? 0;
218
+ const dashArray = `${toPx(len)} ${toPx(spa)}`;
219
+ const dashOffset = negatePx(off);
220
+ // logging removed
221
+ return {
222
+ pathLength: '1',
223
+ 'stroke-dasharray': dashArray,
224
+ 'stroke-dashoffset': dashOffset
225
+ };
226
+ };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@humanspeak/svelte-motion",
3
- "version": "0.1.10",
3
+ "version": "0.1.12",
4
4
  "description": "A lightweight animation library for Svelte 5 that provides smooth, hardware-accelerated animations. Features include spring physics, custom easing, and fluid transitions. Built on top of the motion library, it offers a simple API for creating complex animations with minimal code. Perfect for interactive UIs, micro-interactions, and engaging user experiences.",
5
5
  "keywords": [
6
6
  "svelte",
@@ -59,9 +59,9 @@
59
59
  "@changesets/cli": "^2.29.7",
60
60
  "@eslint/compat": "^1.4.0",
61
61
  "@eslint/js": "^9.37.0",
62
- "@playwright/test": "^1.55.1",
62
+ "@playwright/test": "^1.56.0",
63
63
  "@sveltejs/adapter-auto": "^6.1.1",
64
- "@sveltejs/kit": "^2.43.8",
64
+ "@sveltejs/kit": "^2.46.4",
65
65
  "@sveltejs/package": "^2.5.4",
66
66
  "@sveltejs/vite-plugin-svelte": "^6.2.1",
67
67
  "@tailwindcss/aspect-ratio": "^0.4.2",
@@ -71,7 +71,7 @@
71
71
  "@tailwindcss/typography": "^0.5.19",
72
72
  "@testing-library/jest-dom": "^6.9.1",
73
73
  "@testing-library/svelte": "^5.2.8",
74
- "@types/node": "^24.6.2",
74
+ "@types/node": "^24.7.1",
75
75
  "@vitest/coverage-v8": "^3.2.4",
76
76
  "concurrently": "^9.2.1",
77
77
  "eslint": "^9.37.0",
@@ -89,9 +89,9 @@
89
89
  "prettier-plugin-sort-json": "^4.1.1",
90
90
  "prettier-plugin-svelte": "^3.4.0",
91
91
  "prettier-plugin-tailwindcss": "^0.6.14",
92
- "publint": "^0.3.13",
93
- "svelte": "^5.39.8",
94
- "svelte-check": "^4.3.2",
92
+ "publint": "^0.3.14",
93
+ "svelte": "^5.39.11",
94
+ "svelte-check": "^4.3.3",
95
95
  "svg-tags": "^1.0.0",
96
96
  "tailwind-merge": "^3.3.1",
97
97
  "tailwind-variants": "^3.1.1",
@@ -99,7 +99,7 @@
99
99
  "tailwindcss-animate": "^1.0.7",
100
100
  "tsx": "^4.20.6",
101
101
  "typescript": "^5.9.3",
102
- "typescript-eslint": "^8.45.0",
102
+ "typescript-eslint": "^8.46.0",
103
103
  "vite": "^7.1.9",
104
104
  "vite-tsconfig-paths": "^5.1.4",
105
105
  "vitest": "^3.2.4"