motion-components
v0.5.0
Published
Framework-agnostic web components with physics-based motion. Built on Motion and Lit.
Maintainers
Readme
motion-components
Drop-in web components with spring-physics animations. Works in any framework - or no framework at all.
Architected with animation as a core concern from day one - not bolted on as an afterthought. Every component leverages spring physics, interruptible animations, and composable primitives to deliver interactions that feel natural and responsive.
▸ Reveal elements on scroll ▸ Hover/press/tilt responses ▸ Character-by-character text effects ▸ Parallax & scroll scenes ▸ Sliders, dialogs, image comparisons & more
Built with Motion and Lit. Full TypeScript types included.
Quick start
1. Install
npm install motion-components2. Import & use
// Import all components
import 'motion-components'
// Import separate components (tree shakable)
import 'motion-components/motion-reveal'
import 'motion-components/motion-hover'
import 'motion-components/motion-stagger'
// Prevent content flash before animations run
import 'motion-components/preload.css'<motion-reveal>
<h1>Animates in when scrolled into view</h1>
</motion-reveal>
<motion-hover scale="1.05">
<button>Hover me</button>
</motion-hover>
<motion-stagger interval="0.06">
<p>First child</p>
<p>Second child</p>
<p>Third child</p>
</motion-stagger>CDN - no build step
<script type="module" src="https://cdn.jsdelivr.net/npm/motion-components/dist/index.js"></script>
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/motion-components/dist/preload.css" />
<motion-reveal>
<h1>Animates in when scrolled into view</h1>
</motion-reveal>Framework setup
| Framework | Config needed? |
| -------------------------------- | ------------------------------------------------------------------------------------------------- |
| Astro, Svelte, Solid, Preact | None |
| React | Use React 19+ (native web component support). React 18 has limited support - upgrade if possible. |
| Vue 3 | Tell the compiler to treat motion-* as custom elements |
| Angular | Add CUSTOM_ELEMENTS_SCHEMA |
| Plain HTML | Use the CDN script tag above |
// vite.config.js
vue({
template: {
compilerOptions: {
isCustomElement: (tag) => tag.startsWith('motion-'),
},
},
})import { CUSTOM_ELEMENTS_SCHEMA, Component } from '@angular/core'
@Component({
schemas: [CUSTOM_ELEMENTS_SCHEMA],
})
export class MyComponent {}Components
Reveal 🔗
Entrance & transition effects
motion-reveal motion-stagger motion-blur motion-blur-in
Respond 🔗
Input-driven interactivity
motion-hover motion-press motion-magnetic motion-tilt
Text 🔗
Typography & character effects
motion-split motion-typewriter motion-counter motion-scramble
motion-ticker motion-words motion-curve motion-circle
motion-arc motion-headline motion-glitch motion-gravity
motion-liquid motion-perspective motion-stretch motion-swap
motion-text-mask motion-font
Scroll 🔗
Scroll-driven animation
Components 🔗
Ready-made interactive components
motion-slider motion-gallery motion-dialog motion-countdown
motion-spotlight motion-progress motion-image-compare motion-flip-card
Code 🔗
Syntax-highlighted code display
motion-code motion-code-inline
JavaScript API
Every animation component implements the same playback interface, so you can drive animations imperatively:
const el = document.querySelector('motion-typewriter')
await el.play() // start if idle/finished, resume if paused
el.pause() // freeze in place
el.finish() // jump to the end state
el.cancel() // reset to the initial state
el.playState // 'idle' | 'running' | 'paused' | 'finished'
await el.finished // per-run promise, resolves on finish or cancelComponents emit motion-start, motion-finish, and motion-cancel events (bubbling and composed), so you can also listen at any ancestor:
document.addEventListener('motion-finish', (e) => console.log(e.target, 'done'))Input-reactive components (motion-hover, motion-press, motion-magnetic, motion-tilt) expose a disabled property/attribute instead — set it to make them settle and stop responding to input.
Global controls
import { pauseAll, resumeAll, cancelAll } from 'motion-components'
pauseAll() // pause every running animation, disable input-reactive components
resumeAll() // resume what pauseAll paused, re-enable what it disabled
cancelAll() // reset everything to its initial state
pauseAll(sidebar) // all three accept a root node to scope the effectWhen prefers-reduced-motion: reduce is set, play() skips straight to the final state — the finished promise and events still fire, so control flow keeps working.
Flash prevention (FOUC)
Web components upgrade asynchronously, so content can flash before animations are ready. Choose your prevention method:
Static stylesheet
import 'motion-components/preload.css'CDN link
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/motion-components/dist/preload.css" />Programmatic
import { preload } from 'motion-components'
preload(['motion-reveal', 'motion-split', 'motion-dialog'])Call preload() with no arguments to cover all preload-registered components.
Raw CSS string (for Astro, Svelte, etc.)
import { preloadCSS } from 'motion-components'
// inject preloadCSS into your framework's <head> mechanismWhich components need preloading? Only those that hide content on initialization - reveal, text, stagger, dialog, and a few widgets. The build validates preload entries automatically via npm run check:preload.
Why motion-components?
- Motion-first. Built around animation from the start, not retrofitted.
- Spring physics by default. No linear easing, no jank.
- Interruptible. Interactions never queue or stutter - even mid-animation.
- Per-component imports. Each component is a standalone subpath export - no dead weight, no bundler magic required.
- Composable. Shared primitives instead of reimplemented animations.
- Accessible. Honors
prefers-reduced-motion. Keyboard-navigable.
Development
npm install # install dependencies
npm run build # build library to ./dist
npm run dev # rebuild on file change
npm run typecheck # tsc --noEmit
npm run lint # ESLint
npm run format # Prettier
npm run check:preload # validate FOUC preload rules
npm run size # bundle size budget checkRepo layout
src/
├── reveal/ entrance & transition effects
├── respond/ input-driven interactivity
├── text/ typography effects
├── scroll/ scroll-driven components
├── components/ ready-made widgets
└── code/ code-display componentsSee CONTRIBUTING.md for component authoring conventions.
Documentation
Showcase
Showcase 🔗 · Parallax 🔗 · Scene 🔗 · Motion Font 🔗
License
MIT © Tanja Gomilar
