{"id":26078185,"url":"https://github.com/humanspeak/svelte-motion","last_synced_at":"2026-04-26T21:01:06.726Z","repository":{"id":278684831,"uuid":"936398297","full_name":"humanspeak/svelte-motion","owner":"humanspeak","description":"An API-compatible Svelte wrapper for Framer Motion’s React API — bring Motion components, variants, transitions, gestures, and layout animations to Svelte via a thin compatibility layer.","archived":false,"fork":false,"pushed_at":"2026-03-29T00:00:00.000Z","size":4145,"stargazers_count":48,"open_issues_count":0,"forks_count":1,"subscribers_count":1,"default_branch":"main","last_synced_at":"2026-04-03T03:57:50.856Z","etag":null,"topics":["animation","api-wrapper","framer-motion","gestures","layout-animations","motion","svelte","sveltekit","transitions"],"latest_commit_sha":null,"homepage":"https://motion.svelte.page","language":"TypeScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/humanspeak.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":".github/FUNDING.yml","license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":"AGENTS.md","dco":null,"cla":null},"funding":{"github":["humanspeak"]}},"created_at":"2025-02-21T02:38:20.000Z","updated_at":"2026-03-29T00:00:03.000Z","dependencies_parsed_at":"2026-02-14T12:20:37.075Z","dependency_job_id":null,"html_url":"https://github.com/humanspeak/svelte-motion","commit_stats":null,"previous_names":["humanspeak/svelte-motion"],"tags_count":42,"template":false,"template_full_name":null,"purl":"pkg:github/humanspeak/svelte-motion","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/humanspeak%2Fsvelte-motion","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/humanspeak%2Fsvelte-motion/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/humanspeak%2Fsvelte-motion/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/humanspeak%2Fsvelte-motion/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/humanspeak","download_url":"https://codeload.github.com/humanspeak/svelte-motion/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/humanspeak%2Fsvelte-motion/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":32312505,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-04-26T19:15:34.056Z","status":"ssl_error","status_checked_at":"2026-04-26T19:15:15.467Z","response_time":129,"last_error":"SSL_read: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["animation","api-wrapper","framer-motion","gestures","layout-animations","motion","svelte","sveltekit","transitions"],"created_at":"2025-03-09T03:50:12.138Z","updated_at":"2026-04-26T21:01:06.682Z","avatar_url":"https://github.com/humanspeak.png","language":"TypeScript","funding_links":["https://github.com/sponsors/humanspeak"],"categories":[],"sub_categories":[],"readme":"# Svelte Motion — Framer Motion API for Svelte 5\n\n[![NPM version](https://img.shields.io/npm/v/@humanspeak/svelte-motion.svg)](https://www.npmjs.com/package/@humanspeak/svelte-motion)\n[![Build Status](https://github.com/humanspeak/svelte-motion/actions/workflows/npm-publish.yml/badge.svg)](https://github.com/humanspeak/svelte-motion/actions/workflows/npm-publish.yml)\n[![Coverage Status](https://coveralls.io/repos/github/humanspeak/svelte-motion/badge.svg?branch=main)](https://coveralls.io/github/humanspeak/svelte-motion?branch=main)\n[![License](https://img.shields.io/npm/l/@humanspeak/svelte-motion.svg)](https://github.com/humanspeak/svelte-motion/blob/main/LICENSE)\n[![Downloads](https://img.shields.io/npm/dm/@humanspeak/svelte-motion.svg)](https://www.npmjs.com/package/@humanspeak/svelte-motion)\n[![CodeQL](https://github.com/humanspeak/svelte-motion/actions/workflows/codeql.yml/badge.svg)](https://github.com/humanspeak/svelte-motion/actions/workflows/codeql.yml)\n[![Install size](https://packagephobia.com/badge?p=@humanspeak/svelte-motion)](https://packagephobia.com/result?p=@humanspeak/svelte-motion)\n[![Code Style: Trunk](https://img.shields.io/badge/code%20style-trunk-blue.svg)](https://trunk.io)\n[![TypeScript](https://img.shields.io/badge/%3C%2F%3E-TypeScript-%230074c1.svg)](http://www.typescriptlang.org/)\n[![Types](https://img.shields.io/npm/types/@humanspeak/svelte-motion.svg)](https://www.npmjs.com/package/@humanspeak/svelte-motion)\n[![Maintenance](https://img.shields.io/badge/Maintained%3F-yes-green.svg)](https://github.com/humanspeak/svelte-motion/graphs/commit-activity)\n\nSvelte Motion brings a Framer Motion-style API to Svelte 5 with `motion.\u003ctag\u003e` components, gestures, variants, exit animations, layout animation, and utility hooks.\n\nFor the latest documentation and examples, visit [motion.svelte.page](https://motion.svelte.page).\n\n## Install\n\n```bash\nnpm install @humanspeak/svelte-motion\n```\n\n```svelte\n\u003cscript lang=\"ts\"\u003e\n    import { motion } from '@humanspeak/svelte-motion'\n\u003c/script\u003e\n\n\u003cmotion.button initial={{ opacity: 0 }} animate={{ opacity: 1 }} whileTap={{ scale: 0.97 }}\u003e\n    Hello motion\n\u003c/motion.button\u003e\n```\n\n## Framer Motion API Parity\n\nGoal: Framer Motion API parity for Svelte where common React examples can be translated with minimal changes.\n\n| Capability                                                | Status                          |\n| --------------------------------------------------------- | ------------------------------- |\n| `initial` / `animate` / `transition`                      | Supported                       |\n| `variants` (string keys + inheritance)                    | Supported                       |\n| `whileHover` / `whileTap` / `whileFocus` / `whileInView`  | Supported                       |\n| Drag (`drag`, constraints, momentum, controls, callbacks) | Supported                       |\n| `AnimatePresence` (`initial`, `mode`, `onExitComplete`)   | Supported                       |\n| Layout (`layout`, `layout=\"position\"`)                    | Supported (single-element FLIP) |\n| Shared layout (`layoutId`)                                | Supported                       |\n| Pan gesture API (`whilePan`, `onPan*`)                    | Not yet supported               |\n| `MotionConfig` parity beyond `transition`                 | Partial                         |\n| `reducedMotion`, `features`, `transformPagePoint`         | Not yet supported               |\n\n## Supported elements\n\nMotion components are generated from canonical HTML/SVG tag lists and exported from `src/lib/html/`.\n\n- `motion.div`, `motion.button`, `motion.svg`, `motion.path`, etc.\n- Most standard tags are included.\n- Excluded by generation: `script`, `style`, `link`, `meta`, `title`, `head`, `html`, `body`.\n\n## Core components\n\n### `motion.\u003ctag\u003e`\n\nUse motion components the same way you use regular elements, with animation props:\n\n```svelte\n\u003cmotion.div\n    initial={{ opacity: 0, y: 8 }}\n    animate={{ opacity: 1, y: 0 }}\n    transition={{ duration: 0.25 }}\n/\u003e\n```\n\n### `MotionConfig`\n\n`MotionConfig` currently supports default `transition` values for descendants.\n\n```svelte\n\u003cscript lang=\"ts\"\u003e\n    import { MotionConfig, motion } from '@humanspeak/svelte-motion'\n\u003c/script\u003e\n\n\u003cMotionConfig transition={{ duration: 0.4 }}\u003e\n    \u003cmotion.div animate={{ scale: 1.05 }} /\u003e\n\u003c/MotionConfig\u003e\n```\n\n### `AnimatePresence`\n\nExit animations on unmount with support for `mode=\"sync\" | \"wait\" | \"popLayout\"` and `onExitComplete`.\n\n```svelte\n\u003cscript lang=\"ts\"\u003e\n    import { AnimatePresence, motion } from '@humanspeak/svelte-motion'\n\n    let show = $state(true)\n\u003c/script\u003e\n\n\u003cAnimatePresence mode=\"wait\" onExitComplete={() =\u003e console.log('done')}\u003e\n    {#if show}\n        \u003cmotion.div\n            key=\"card\"\n            initial={{ opacity: 0, scale: 0.9 }}\n            animate={{ opacity: 1, scale: 1 }}\n            exit={{ opacity: 0, scale: 0.9, transition: { duration: 0.2 } }}\n        /\u003e\n    {/if}\n\u003c/AnimatePresence\u003e\n\n\u003cmotion.button whileTap={{ scale: 0.97 }} onclick={() =\u003e (show = !show)}\u003eToggle\u003c/motion.button\u003e\n```\n\nNotes:\n\n- Direct children of `AnimatePresence` require `key`.\n- Exit transition precedence: base `{ duration: 0.35 }` \u003c merged `transition` \u003c `exit.transition`.\n\n## Interaction props\n\n### `whileHover`\n\n```svelte\n\u003cmotion.button whileHover={{ scale: 1.05, transition: { duration: 0.12 } }} /\u003e\n```\n\n- Uses true-hover gating (`(hover: hover)` and `(pointer: fine)`).\n- Supports `onHoverStart` and `onHoverEnd`.\n\n### `whileTap`\n\n```svelte\n\u003cmotion.button whileTap={{ scale: 0.95 }} /\u003e\n```\n\n- Supports `onTapStart`, `onTap`, `onTapCancel`.\n- Keyboard accessible (Enter/Space).\n\n### `whileFocus`\n\n```svelte\n\u003cmotion.button whileFocus={{ scale: 1.05, outline: '2px solid blue' }} /\u003e\n```\n\n- Supports `onFocusStart` and `onFocusEnd`.\n\n### `whileInView`\n\n```svelte\n\u003cmotion.div\n    initial={{ opacity: 0, y: 40 }}\n    whileInView={{ opacity: 1, y: 0 }}\n    onInViewStart={() =\u003e console.log('entered')}\n    onInViewEnd={() =\u003e console.log('left')}\n/\u003e\n```\n\n- Uses `IntersectionObserver`.\n- Current implementation uses a fixed threshold behavior (no Framer-style `viewport` options yet).\n\n## Drag\n\nSupported drag props:\n\n- `drag`: `true | 'x' | 'y'`\n- `dragConstraints`: pixel object or element ref\n- `dragElastic`, `dragMomentum`, `dragTransition`\n- `dragDirectionLock`, `dragPropagation`, `dragSnapToOrigin`\n- `dragListener`, `dragControls`\n- `whileDrag`\n- Callbacks: `onDragStart`, `onDrag`, `onDragEnd`, `onDirectionLock`, `onDragTransitionEnd`\n\n```svelte\n\u003cscript lang=\"ts\"\u003e\n    import { createDragControls, motion } from '@humanspeak/svelte-motion'\n\n    const controls = createDragControls()\n\u003c/script\u003e\n\n\u003cbutton onpointerdown={(e) =\u003e controls.start(e)}\u003eStart drag\u003c/button\u003e\n\n\u003cmotion.div\n    drag=\"x\"\n    dragControls={controls}\n    dragListener={false}\n    dragConstraints={{ left: -120, right: 120 }}\n    whileDrag={{ scale: 1.03 }}\n/\u003e\n```\n\n## Variants\n\n```svelte\n\u003cscript lang=\"ts\"\u003e\n    import { motion, type Variants } from '@humanspeak/svelte-motion'\n\n    let open = $state(false)\n\n    const parent: Variants = {\n        open: { opacity: 1 },\n        closed: { opacity: 0 }\n    }\n\n    const child: Variants = {\n        open: { x: 0, opacity: 1 },\n        closed: { x: -16, opacity: 0 }\n    }\n\u003c/script\u003e\n\n\u003cmotion.ul variants={parent} initial=\"closed\" animate={open ? 'open' : 'closed'}\u003e\n    \u003cmotion.li variants={child}\u003eItem A\u003c/motion.li\u003e\n    \u003cmotion.li variants={child}\u003eItem B\u003c/motion.li\u003e\n\u003c/motion.ul\u003e\n```\n\n- String variant keys are resolved from `variants`.\n- Variant state inherits through context.\n\n## Layout animation\n\nSingle-element FLIP layout animation:\n\n```svelte\n\u003cmotion.div layout /\u003e\n\u003cmotion.div layout=\"position\" /\u003e\n```\n\n- `layout`: translate + scale.\n- `layout=\"position\"`: translate only.\n- Shared layout (`layoutId`) is not implemented yet.\n\n## Utilities\n\n- `useAnimationFrame`\n- `useMotionTemplate`\n- `useSpring`\n- `useTime`\n- `useTransform`\n- `useVelocity`\n- `styleString`\n- `stringifyStyleObject` (deprecated)\n- `createDragControls`\n\nThe package also re-exports core helpers from `motion` (for example `animate`, `stagger`, `transform`, easings, and utility functions).\n\n## SSR behavior\n\n- Initial visual state is rendered server-side from `initial` (or first `animate` keyframe when `initial` is empty).\n- `initial={false}` skips initial enter animation.\n- Hydration path is designed to avoid flicker.\n\n## Verification snapshot\n\nValidated against current source and test suite (local run):\n\n- Unit/component tests: `259 passed`\n- E2E tests: `78 passed`, `1 skipped`\n\n## Known gaps vs Framer Motion\n\n- No shared layout API (`layoutId`, `LayoutGroup`).\n- No pan gesture API (`whilePan`, `onPan*`).\n- `whileInView` does not yet expose Framer-style viewport options.\n- `MotionConfig` currently only provides `transition` defaults.\n- `reducedMotion`, `features`, and `transformPagePoint` are not implemented.\n\n## External dependencies\n\n- `motion`\n- `motion-dom`\n\n## License\n\nMIT © [Humanspeak, Inc.](LICENSE)\n\n## Credits\n\nMade with ❤️ by [Humanspeak](https://humanspeak.com)\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fhumanspeak%2Fsvelte-motion","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fhumanspeak%2Fsvelte-motion","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fhumanspeak%2Fsvelte-motion/lists"}