@humanspeak/svelte-motion
v0.2.0
Published
A Framer Motion-compatible 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
Maintainers
Readme
Svelte Motion — Framer Motion API for Svelte 5
Svelte Motion brings a Framer Motion-style API to Svelte 5 with motion.<tag> components, gestures, variants, exit animations, layout animation, and utility hooks.
For the latest documentation and examples, visit motion.svelte.page.
Install
npm install @humanspeak/svelte-motion<script lang="ts">
import { motion } from '@humanspeak/svelte-motion'
</script>
<motion.button initial={{ opacity: 0 }} animate={{ opacity: 1 }} whileTap={{ scale: 0.97 }}>
Hello motion
</motion.button>Framer Motion API Parity
Goal: Framer Motion API parity for Svelte where common React examples can be translated with minimal changes.
| Capability | Status |
| --------------------------------------------------------- | ------------------------------- |
| initial / animate / transition | Supported |
| variants (string keys + inheritance) | Supported |
| whileHover / whileTap / whileFocus / whileInView | Supported |
| Drag (drag, constraints, momentum, controls, callbacks) | Supported |
| AnimatePresence (initial, mode, onExitComplete) | Supported |
| Layout (layout, layout="position") | Supported (single-element FLIP) |
| Shared layout (layoutId) | Supported |
| Pan gesture API (whilePan, onPan*) | Not yet supported |
| MotionConfig parity beyond transition | Partial |
| reducedMotion, features, transformPagePoint | Not yet supported |
Supported elements
Motion components are generated from canonical HTML/SVG tag lists and exported from src/lib/html/.
motion.div,motion.button,motion.svg,motion.path, etc.- Most standard tags are included.
- Excluded by generation:
script,style,link,meta,title,head,html,body.
Core components
motion.<tag>
Use motion components the same way you use regular elements, with animation props:
<motion.div
initial={{ opacity: 0, y: 8 }}
animate={{ opacity: 1, y: 0 }}
transition={{ duration: 0.25 }}
/>MotionConfig
MotionConfig currently supports default transition values for descendants.
<script lang="ts">
import { MotionConfig, motion } from '@humanspeak/svelte-motion'
</script>
<MotionConfig transition={{ duration: 0.4 }}>
<motion.div animate={{ scale: 1.05 }} />
</MotionConfig>AnimatePresence
Exit animations on unmount with support for mode="sync" | "wait" | "popLayout" and onExitComplete.
<script lang="ts">
import { AnimatePresence, motion } from '@humanspeak/svelte-motion'
let show = $state(true)
</script>
<AnimatePresence mode="wait" onExitComplete={() => console.log('done')}>
{#if show}
<motion.div
key="card"
initial={{ opacity: 0, scale: 0.9 }}
animate={{ opacity: 1, scale: 1 }}
exit={{ opacity: 0, scale: 0.9, transition: { duration: 0.2 } }}
/>
{/if}
</AnimatePresence>
<motion.button whileTap={{ scale: 0.97 }} onclick={() => (show = !show)}>Toggle</motion.button>Notes:
- Direct children of
AnimatePresencerequirekey. - Exit transition precedence: base
{ duration: 0.35 }< mergedtransition<exit.transition.
Interaction props
whileHover
<motion.button whileHover={{ scale: 1.05, transition: { duration: 0.12 } }} />- Uses true-hover gating (
(hover: hover)and(pointer: fine)). - Supports
onHoverStartandonHoverEnd.
whileTap
<motion.button whileTap={{ scale: 0.95 }} />- Supports
onTapStart,onTap,onTapCancel. - Keyboard accessible (Enter/Space).
whileFocus
<motion.button whileFocus={{ scale: 1.05, outline: '2px solid blue' }} />- Supports
onFocusStartandonFocusEnd.
whileInView
<motion.div
initial={{ opacity: 0, y: 40 }}
whileInView={{ opacity: 1, y: 0 }}
onInViewStart={() => console.log('entered')}
onInViewEnd={() => console.log('left')}
/>- Uses
IntersectionObserver. - Current implementation uses a fixed threshold behavior (no Framer-style
viewportoptions yet).
Drag
Supported drag props:
drag:true | 'x' | 'y'dragConstraints: pixel object or element refdragElastic,dragMomentum,dragTransitiondragDirectionLock,dragPropagation,dragSnapToOrigindragListener,dragControlswhileDrag- Callbacks:
onDragStart,onDrag,onDragEnd,onDirectionLock,onDragTransitionEnd
<script lang="ts">
import { createDragControls, motion } from '@humanspeak/svelte-motion'
const controls = createDragControls()
</script>
<button onpointerdown={(e) => controls.start(e)}>Start drag</button>
<motion.div
drag="x"
dragControls={controls}
dragListener={false}
dragConstraints={{ left: -120, right: 120 }}
whileDrag={{ scale: 1.03 }}
/>Variants
<script lang="ts">
import { motion, type Variants } from '@humanspeak/svelte-motion'
let open = $state(false)
const parent: Variants = {
open: { opacity: 1 },
closed: { opacity: 0 }
}
const child: Variants = {
open: { x: 0, opacity: 1 },
closed: { x: -16, opacity: 0 }
}
</script>
<motion.ul variants={parent} initial="closed" animate={open ? 'open' : 'closed'}>
<motion.li variants={child}>Item A</motion.li>
<motion.li variants={child}>Item B</motion.li>
</motion.ul>- String variant keys are resolved from
variants. - Variant state inherits through context.
Layout animation
Single-element FLIP layout animation:
<motion.div layout />
<motion.div layout="position" />layout: translate + scale.layout="position": translate only.- Shared layout (
layoutId) is not implemented yet.
Utilities
useAnimationFrameuseMotionTemplateuseSpringuseTimeuseTransformuseVelocitystyleStringstringifyStyleObject(deprecated)createDragControls
The package also re-exports core helpers from motion (for example animate, stagger, transform, easings, and utility functions).
SSR behavior
- Initial visual state is rendered server-side from
initial(or firstanimatekeyframe wheninitialis empty). initial={false}skips initial enter animation.- Hydration path is designed to avoid flicker.
Verification snapshot
Validated against current source and test suite (local run):
- Unit/component tests:
259 passed - E2E tests:
78 passed,1 skipped
Known gaps vs Framer Motion
- No shared layout API (
layoutId,LayoutGroup). - No pan gesture API (
whilePan,onPan*). whileInViewdoes not yet expose Framer-style viewport options.MotionConfigcurrently only providestransitiondefaults.reducedMotion,features, andtransformPagePointare not implemented.
External dependencies
motionmotion-dom
License
MIT © Humanspeak, Inc.
Credits
Made with ❤️ by Humanspeak
