@humanspeak/svelte-motion 0.5.3 → 0.5.4
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.
- package/dist/components/LazyMotion.svelte +70 -0
- package/dist/components/LazyMotion.svelte.d.ts +16 -0
- package/dist/components/lazyMotion.context.d.ts +25 -0
- package/dist/components/lazyMotion.context.js +19 -0
- package/dist/features/domAnimation.d.ts +6 -0
- package/dist/features/domAnimation.js +8 -0
- package/dist/features/domMax.d.ts +5 -0
- package/dist/features/domMax.js +9 -0
- package/dist/features/domMin.d.ts +5 -0
- package/dist/features/domMin.js +6 -0
- package/dist/features/index.d.ts +39 -0
- package/dist/features/index.js +18 -0
- package/dist/html/_MotionContainer.svelte +53 -9
- package/dist/index.d.ts +7 -1
- package/dist/index.js +6 -1
- package/dist/m.d.ts +9 -0
- package/dist/m.js +9 -0
- package/package.json +1 -1
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
import { setLazyMotionContext } from './lazyMotion.context'
|
|
3
|
+
import { domMin } from '../features/domMin'
|
|
4
|
+
import {
|
|
5
|
+
isLazyFeatureBundle,
|
|
6
|
+
normalizeLazyFeatureBundle,
|
|
7
|
+
type FeatureBundle,
|
|
8
|
+
type LazyFeatureBundle
|
|
9
|
+
} from '../features'
|
|
10
|
+
import { untrack, type Snippet } from 'svelte'
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* Props accepted by the LazyMotion component.
|
|
14
|
+
*/
|
|
15
|
+
type Props = {
|
|
16
|
+
/** Child content rendered inside the active LazyMotion context. */
|
|
17
|
+
children?: Snippet
|
|
18
|
+
/** Eager or async feature bundle used by descendant `m.*` components. */
|
|
19
|
+
features: FeatureBundle | LazyFeatureBundle
|
|
20
|
+
/** Enables strict LazyMotion usage checks. Defaults to false. */
|
|
21
|
+
strict?: boolean
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
let { children, features, strict = false }: Props = $props()
|
|
25
|
+
|
|
26
|
+
let loadedFeatures = $state<FeatureBundle>(
|
|
27
|
+
untrack(() => (isLazyFeatureBundle(features) ? domMin : features))
|
|
28
|
+
)
|
|
29
|
+
let isLoaded = $state(untrack(() => !isLazyFeatureBundle(features)))
|
|
30
|
+
|
|
31
|
+
setLazyMotionContext({
|
|
32
|
+
getFeatures: () => loadedFeatures,
|
|
33
|
+
getIsLoaded: () => isLoaded,
|
|
34
|
+
get strict() {
|
|
35
|
+
return strict
|
|
36
|
+
}
|
|
37
|
+
})
|
|
38
|
+
|
|
39
|
+
let loadId = 0
|
|
40
|
+
|
|
41
|
+
$effect(() => {
|
|
42
|
+
const currentLoadId = ++loadId
|
|
43
|
+
|
|
44
|
+
if (!isLazyFeatureBundle(features)) {
|
|
45
|
+
loadedFeatures = features
|
|
46
|
+
isLoaded = true
|
|
47
|
+
return
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
loadedFeatures = domMin
|
|
51
|
+
isLoaded = false
|
|
52
|
+
features()
|
|
53
|
+
.then((bundle) => {
|
|
54
|
+
if (currentLoadId !== loadId) return
|
|
55
|
+
loadedFeatures = normalizeLazyFeatureBundle(bundle)
|
|
56
|
+
isLoaded = true
|
|
57
|
+
})
|
|
58
|
+
.catch(() => {
|
|
59
|
+
if (currentLoadId !== loadId) return
|
|
60
|
+
loadedFeatures = domMin
|
|
61
|
+
isLoaded = false
|
|
62
|
+
})
|
|
63
|
+
|
|
64
|
+
return () => {
|
|
65
|
+
loadId += 1
|
|
66
|
+
}
|
|
67
|
+
})
|
|
68
|
+
</script>
|
|
69
|
+
|
|
70
|
+
{@render children?.()}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { type FeatureBundle, type LazyFeatureBundle } from '../features';
|
|
2
|
+
import { type Snippet } from 'svelte';
|
|
3
|
+
/**
|
|
4
|
+
* Props accepted by the LazyMotion component.
|
|
5
|
+
*/
|
|
6
|
+
type Props = {
|
|
7
|
+
/** Child content rendered inside the active LazyMotion context. */
|
|
8
|
+
children?: Snippet;
|
|
9
|
+
/** Eager or async feature bundle used by descendant `m.*` components. */
|
|
10
|
+
features: FeatureBundle | LazyFeatureBundle;
|
|
11
|
+
/** Enables strict LazyMotion usage checks. Defaults to false. */
|
|
12
|
+
strict?: boolean;
|
|
13
|
+
};
|
|
14
|
+
declare const LazyMotion: import("svelte").Component<Props, {}, "">;
|
|
15
|
+
type LazyMotion = ReturnType<typeof LazyMotion>;
|
|
16
|
+
export default LazyMotion;
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import type { FeatureBundle } from '../features';
|
|
2
|
+
/**
|
|
3
|
+
* Context value published by `<LazyMotion>` for descendant motion elements.
|
|
4
|
+
*/
|
|
5
|
+
export type LazyMotionContext = {
|
|
6
|
+
/** Returns the currently active feature bundle. */
|
|
7
|
+
getFeatures: () => FeatureBundle;
|
|
8
|
+
/** Returns whether an async bundle has finished loading. */
|
|
9
|
+
getIsLoaded: () => boolean;
|
|
10
|
+
/** Enables Framer Motion-style strict lazy usage checks. */
|
|
11
|
+
strict: boolean;
|
|
12
|
+
};
|
|
13
|
+
/**
|
|
14
|
+
* Reads the nearest LazyMotion context.
|
|
15
|
+
*
|
|
16
|
+
* @returns The active LazyMotion context, or undefined outside LazyMotion.
|
|
17
|
+
*/
|
|
18
|
+
export declare const getLazyMotionContext: () => LazyMotionContext | undefined;
|
|
19
|
+
/**
|
|
20
|
+
* Publishes a LazyMotion context for descendant motion elements.
|
|
21
|
+
*
|
|
22
|
+
* @param context - LazyMotion context to publish.
|
|
23
|
+
* @returns The same context value returned by Svelte's setContext.
|
|
24
|
+
*/
|
|
25
|
+
export declare const setLazyMotionContext: (context: LazyMotionContext) => LazyMotionContext;
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { getContext, setContext } from 'svelte';
|
|
2
|
+
const key = Symbol('lazyMotion');
|
|
3
|
+
/**
|
|
4
|
+
* Reads the nearest LazyMotion context.
|
|
5
|
+
*
|
|
6
|
+
* @returns The active LazyMotion context, or undefined outside LazyMotion.
|
|
7
|
+
*/
|
|
8
|
+
export const getLazyMotionContext = () => {
|
|
9
|
+
return getContext(key);
|
|
10
|
+
};
|
|
11
|
+
/**
|
|
12
|
+
* Publishes a LazyMotion context for descendant motion elements.
|
|
13
|
+
*
|
|
14
|
+
* @param context - LazyMotion context to publish.
|
|
15
|
+
* @returns The same context value returned by Svelte's setContext.
|
|
16
|
+
*/
|
|
17
|
+
export const setLazyMotionContext = (context) => {
|
|
18
|
+
return setContext(key, context);
|
|
19
|
+
};
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Runtime capabilities made available to motion components inside a
|
|
3
|
+
* `<LazyMotion>` subtree.
|
|
4
|
+
*/
|
|
5
|
+
export type FeatureBundle = {
|
|
6
|
+
/** Enables initial/animate/exit animation behavior. */
|
|
7
|
+
animations: true;
|
|
8
|
+
/** Enables hover, tap, focus, pan, and in-view gesture behavior. */
|
|
9
|
+
gestures?: true;
|
|
10
|
+
/** Enables drag gesture behavior. */
|
|
11
|
+
drag?: true;
|
|
12
|
+
/** Enables layout and shared-layout animation behavior. */
|
|
13
|
+
layout?: true;
|
|
14
|
+
};
|
|
15
|
+
/**
|
|
16
|
+
* Function form accepted by `<LazyMotion features>`.
|
|
17
|
+
*
|
|
18
|
+
* The function resolves to a feature bundle directly or to a module-like
|
|
19
|
+
* object with the bundle as its default export.
|
|
20
|
+
*/
|
|
21
|
+
export type LazyFeatureBundle = () => Promise<FeatureBundle | {
|
|
22
|
+
default: FeatureBundle;
|
|
23
|
+
}>;
|
|
24
|
+
/**
|
|
25
|
+
* Returns whether a LazyMotion `features` value is an async loader.
|
|
26
|
+
*
|
|
27
|
+
* @param features - Feature bundle or loader passed to `<LazyMotion>`.
|
|
28
|
+
* @returns True when the features value should be invoked asynchronously.
|
|
29
|
+
*/
|
|
30
|
+
export declare const isLazyFeatureBundle: (features: FeatureBundle | LazyFeatureBundle) => features is LazyFeatureBundle;
|
|
31
|
+
/**
|
|
32
|
+
* Normalizes an asynchronously loaded feature bundle.
|
|
33
|
+
*
|
|
34
|
+
* @param loaded - Resolved bundle or default-export module wrapper.
|
|
35
|
+
* @returns The concrete feature bundle.
|
|
36
|
+
*/
|
|
37
|
+
export declare const normalizeLazyFeatureBundle: (loaded: FeatureBundle | {
|
|
38
|
+
default: FeatureBundle;
|
|
39
|
+
}) => FeatureBundle;
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Returns whether a LazyMotion `features` value is an async loader.
|
|
3
|
+
*
|
|
4
|
+
* @param features - Feature bundle or loader passed to `<LazyMotion>`.
|
|
5
|
+
* @returns True when the features value should be invoked asynchronously.
|
|
6
|
+
*/
|
|
7
|
+
export const isLazyFeatureBundle = (features) => typeof features === 'function';
|
|
8
|
+
/**
|
|
9
|
+
* Normalizes an asynchronously loaded feature bundle.
|
|
10
|
+
*
|
|
11
|
+
* @param loaded - Resolved bundle or default-export module wrapper.
|
|
12
|
+
* @returns The concrete feature bundle.
|
|
13
|
+
*/
|
|
14
|
+
export const normalizeLazyFeatureBundle = (loaded) => {
|
|
15
|
+
if ('default' in loaded)
|
|
16
|
+
return loaded.default;
|
|
17
|
+
return loaded;
|
|
18
|
+
};
|
|
@@ -5,6 +5,8 @@
|
|
|
5
5
|
|
|
6
6
|
<script lang="ts">
|
|
7
7
|
import { getMotionConfig } from '../components/motionConfig.context'
|
|
8
|
+
import { getLazyMotionContext } from '../components/lazyMotion.context'
|
|
9
|
+
import { domMax } from '../features/domMax'
|
|
8
10
|
import {
|
|
9
11
|
filterReducedMotionKeyframes,
|
|
10
12
|
useReducedMotionConfig
|
|
@@ -156,6 +158,11 @@
|
|
|
156
158
|
let enterAnimationSettled = $state(false)
|
|
157
159
|
let dataPath = $state<number>(-1)
|
|
158
160
|
const motionConfig = $derived(getMotionConfig())
|
|
161
|
+
const lazyMotion = getLazyMotionContext()
|
|
162
|
+
const activeFeatures = $derived(lazyMotion?.getFeatures() ?? domMax)
|
|
163
|
+
const hasGestureFeatures = $derived(!!activeFeatures.gestures)
|
|
164
|
+
const hasDragFeatures = $derived(!!activeFeatures.drag)
|
|
165
|
+
const hasLayoutFeatures = $derived(!!activeFeatures.layout)
|
|
159
166
|
const reducedMotionState = useReducedMotionConfig()
|
|
160
167
|
// `.current` is $state-backed inside reducedMotionState; tracking it via
|
|
161
168
|
// $derived makes `reducedMotion` re-evaluate whenever the OS preference
|
|
@@ -553,7 +560,8 @@
|
|
|
553
560
|
// key, empty array) would otherwise add `tabindex=0` for an
|
|
554
561
|
// element that never actually receives a tap gesture — an
|
|
555
562
|
// unintended tab stop. (#349 CR feedback)
|
|
556
|
-
...(
|
|
563
|
+
...(hasGestureFeatures &&
|
|
564
|
+
isNotEmpty(resolvedWhileTap) &&
|
|
557
565
|
!isNativelyFocusable(tag, rest as Record<string, unknown>) &&
|
|
558
566
|
((rest as Record<string, unknown>)?.tabindex ??
|
|
559
567
|
(rest as Record<string, unknown>)?.tabIndex ??
|
|
@@ -626,7 +634,7 @@
|
|
|
626
634
|
// after any non-zero duration settle animation.
|
|
627
635
|
let teardownDrag: (() => void) | null = null
|
|
628
636
|
$effect(() => {
|
|
629
|
-
if (!(element && isLoaded === 'ready')) return
|
|
637
|
+
if (!(element && isLoaded === 'ready' && hasDragFeatures)) return
|
|
630
638
|
// Only attach if drag enabled
|
|
631
639
|
if (!dragProp) return
|
|
632
640
|
// Clean up previous
|
|
@@ -787,7 +795,7 @@
|
|
|
787
795
|
isLoaded
|
|
788
796
|
})
|
|
789
797
|
}
|
|
790
|
-
if (!element) return
|
|
798
|
+
if (!element || !hasGestureFeatures) return
|
|
791
799
|
// Defer attachment until the element has settled out of the enter
|
|
792
800
|
// animation phase — matches the gate every other gesture effect
|
|
793
801
|
// in this file uses (drag, whileTap, whileHover, whileFocus,
|
|
@@ -1050,7 +1058,7 @@
|
|
|
1050
1058
|
// When layout === true we also scale to smoothly interpolate size changes.
|
|
1051
1059
|
let lastRect: RectLike | null = null
|
|
1052
1060
|
$effect(() => {
|
|
1053
|
-
if (!(element && layoutProp && isLoaded === 'ready')) return
|
|
1061
|
+
if (!(element && layoutProp && isLoaded === 'ready' && hasLayoutFeatures)) return
|
|
1054
1062
|
|
|
1055
1063
|
// Initialize last rect on first ready frame. We measure through the
|
|
1056
1064
|
// projection node rather than `measureRect` directly so the rect is
|
|
@@ -1105,7 +1113,16 @@
|
|
|
1105
1113
|
// Shared layout animation via layoutId.
|
|
1106
1114
|
// On mount, consume the previous snapshot and FLIP from its position.
|
|
1107
1115
|
$effect(() => {
|
|
1108
|
-
if (
|
|
1116
|
+
if (
|
|
1117
|
+
!(
|
|
1118
|
+
element &&
|
|
1119
|
+
scopedLayoutId &&
|
|
1120
|
+
layoutIdRegistry &&
|
|
1121
|
+
isLoaded === 'ready' &&
|
|
1122
|
+
hasLayoutFeatures
|
|
1123
|
+
)
|
|
1124
|
+
)
|
|
1125
|
+
return
|
|
1109
1126
|
|
|
1110
1127
|
const prev = layoutIdRegistry.consume(scopedLayoutId)
|
|
1111
1128
|
if (!prev) return // First appearance, no animation needed
|
|
@@ -1123,7 +1140,10 @@
|
|
|
1123
1140
|
|
|
1124
1141
|
// whileTap handling via motion-dom's press()
|
|
1125
1142
|
$effect(() => {
|
|
1126
|
-
if (
|
|
1143
|
+
if (
|
|
1144
|
+
!(element && isLoaded === 'ready' && hasGestureFeatures && isNotEmpty(resolvedWhileTap))
|
|
1145
|
+
)
|
|
1146
|
+
return
|
|
1127
1147
|
return attachWhileTap(
|
|
1128
1148
|
element!,
|
|
1129
1149
|
(resolvedWhileTap ?? {}) as Record<string, unknown>,
|
|
@@ -1143,7 +1163,15 @@
|
|
|
1143
1163
|
|
|
1144
1164
|
// whileHover handling, gated to true-hover devices to avoid sticky states on touch
|
|
1145
1165
|
$effect(() => {
|
|
1146
|
-
if (
|
|
1166
|
+
if (
|
|
1167
|
+
!(
|
|
1168
|
+
element &&
|
|
1169
|
+
isLoaded === 'ready' &&
|
|
1170
|
+
hasGestureFeatures &&
|
|
1171
|
+
isNotEmpty(resolvedWhileHover)
|
|
1172
|
+
)
|
|
1173
|
+
)
|
|
1174
|
+
return
|
|
1147
1175
|
return attachWhileHover(
|
|
1148
1176
|
element!,
|
|
1149
1177
|
(resolvedWhileHover ?? {}) as Record<string, unknown>,
|
|
@@ -1158,7 +1186,15 @@
|
|
|
1158
1186
|
|
|
1159
1187
|
// whileFocus handling for keyboard focus interactions
|
|
1160
1188
|
$effect(() => {
|
|
1161
|
-
if (
|
|
1189
|
+
if (
|
|
1190
|
+
!(
|
|
1191
|
+
element &&
|
|
1192
|
+
isLoaded === 'ready' &&
|
|
1193
|
+
hasGestureFeatures &&
|
|
1194
|
+
isNotEmpty(resolvedWhileFocus)
|
|
1195
|
+
)
|
|
1196
|
+
)
|
|
1197
|
+
return
|
|
1162
1198
|
return attachWhileFocus(
|
|
1163
1199
|
element!,
|
|
1164
1200
|
(resolvedWhileFocus ?? {}) as Record<string, unknown>,
|
|
@@ -1173,7 +1209,15 @@
|
|
|
1173
1209
|
|
|
1174
1210
|
// whileInView handling for viewport intersection
|
|
1175
1211
|
$effect(() => {
|
|
1176
|
-
if (
|
|
1212
|
+
if (
|
|
1213
|
+
!(
|
|
1214
|
+
element &&
|
|
1215
|
+
isLoaded === 'ready' &&
|
|
1216
|
+
hasGestureFeatures &&
|
|
1217
|
+
isNotEmpty(resolvedWhileInView)
|
|
1218
|
+
)
|
|
1219
|
+
)
|
|
1220
|
+
return
|
|
1177
1221
|
return attachWhileInView(
|
|
1178
1222
|
element!,
|
|
1179
1223
|
(resolvedWhileInView ?? {}) as Record<string, unknown>,
|
package/dist/index.d.ts
CHANGED
|
@@ -1,7 +1,13 @@
|
|
|
1
1
|
import AnimatePresence from './components/AnimatePresence.svelte';
|
|
2
2
|
import LayoutGroup from './components/LayoutGroup.svelte';
|
|
3
|
+
import LazyMotion from './components/LazyMotion.svelte';
|
|
3
4
|
import MotionConfig from './components/MotionConfig.svelte';
|
|
4
5
|
import PresenceChild from './components/PresenceChild.svelte';
|
|
6
|
+
export type { FeatureBundle, LazyFeatureBundle } from './features';
|
|
7
|
+
export { domAnimation } from './features/domAnimation';
|
|
8
|
+
export { domMax } from './features/domMax';
|
|
9
|
+
export { domMin } from './features/domMin';
|
|
10
|
+
export { m } from './m';
|
|
5
11
|
export { motion } from './motion';
|
|
6
12
|
export { animate, delay, hover, inView, press, resize, scroll, stagger, transform } from 'motion';
|
|
7
13
|
export { anticipate, backIn, backInOut, backOut, circIn, circInOut, circOut, cubicBezier, easeIn, easeInOut, easeOut } from 'motion';
|
|
@@ -42,7 +48,7 @@ export { styleString } from './utils/styleObject.svelte';
|
|
|
42
48
|
export { useTime } from './utils/time.svelte';
|
|
43
49
|
export { useTransform } from './utils/transform.svelte';
|
|
44
50
|
export type { MultiTransformer, SingleTransformer, TransformOptions, TransformOutputMap, TransformSource } from './utils/transform.svelte';
|
|
45
|
-
export { AnimatePresence, LayoutGroup, MotionConfig, PresenceChild };
|
|
51
|
+
export { AnimatePresence, LayoutGroup, LazyMotion, MotionConfig, PresenceChild };
|
|
46
52
|
export { default as MotionA } from './html/A.svelte';
|
|
47
53
|
export { default as MotionAbbr } from './html/Abbr.svelte';
|
|
48
54
|
export { default as MotionAddress } from './html/Address.svelte';
|
package/dist/index.js
CHANGED
|
@@ -1,7 +1,12 @@
|
|
|
1
1
|
import AnimatePresence from './components/AnimatePresence.svelte';
|
|
2
2
|
import LayoutGroup from './components/LayoutGroup.svelte';
|
|
3
|
+
import LazyMotion from './components/LazyMotion.svelte';
|
|
3
4
|
import MotionConfig from './components/MotionConfig.svelte';
|
|
4
5
|
import PresenceChild from './components/PresenceChild.svelte';
|
|
6
|
+
export { domAnimation } from './features/domAnimation';
|
|
7
|
+
export { domMax } from './features/domMax';
|
|
8
|
+
export { domMin } from './features/domMin';
|
|
9
|
+
export { m } from './m';
|
|
5
10
|
export { motion } from './motion';
|
|
6
11
|
// Re-export core animation functions from motion
|
|
7
12
|
export { animate, delay, hover, inView, press, resize, scroll, stagger, transform } from 'motion';
|
|
@@ -31,7 +36,7 @@ export { stringifyStyleObject } from './utils/styleObject';
|
|
|
31
36
|
export { styleString } from './utils/styleObject.svelte';
|
|
32
37
|
export { useTime } from './utils/time.svelte';
|
|
33
38
|
export { useTransform } from './utils/transform.svelte';
|
|
34
|
-
export { AnimatePresence, LayoutGroup, MotionConfig, PresenceChild };
|
|
39
|
+
export { AnimatePresence, LayoutGroup, LazyMotion, MotionConfig, PresenceChild };
|
|
35
40
|
// Named component exports — tree-shakeable alternative to the `motion` object
|
|
36
41
|
export { default as MotionA } from './html/A.svelte';
|
|
37
42
|
export { default as MotionAbbr } from './html/Abbr.svelte';
|
package/dist/m.d.ts
ADDED
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import type { MotionComponents } from './html/index';
|
|
2
|
+
/**
|
|
3
|
+
* Lazy motion component namespace used with `<LazyMotion>`.
|
|
4
|
+
*
|
|
5
|
+
* The namespace mirrors the default `motion` object API (`m.div`, `m.button`,
|
|
6
|
+
* `m.svg`, etc.) while reading feature availability from the nearest
|
|
7
|
+
* LazyMotion provider.
|
|
8
|
+
*/
|
|
9
|
+
export declare const m: MotionComponents;
|
package/dist/m.js
ADDED
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import * as html from './html/index';
|
|
2
|
+
/**
|
|
3
|
+
* Lazy motion component namespace used with `<LazyMotion>`.
|
|
4
|
+
*
|
|
5
|
+
* The namespace mirrors the default `motion` object API (`m.div`, `m.button`,
|
|
6
|
+
* `m.svg`, etc.) while reading feature availability from the nearest
|
|
7
|
+
* LazyMotion provider.
|
|
8
|
+
*/
|
|
9
|
+
export const m = Object.fromEntries(Object.entries(html).map(([key, component]) => [key.toLowerCase(), component]));
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@humanspeak/svelte-motion",
|
|
3
|
-
"version": "0.5.
|
|
3
|
+
"version": "0.5.4",
|
|
4
4
|
"description": "Framer Motion for Svelte 5. Declarative motion.<tag> components with AnimatePresence exit animations, gestures (hover, tap, drag, focus, in-view), variants, FLIP layout animations, shared-layout transitions, spring physics, and scroll-linked motion values. The drop-in Framer Motion alternative for Svelte and SvelteKit.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"svelte",
|