A lightweight, performant animation library with CSS-only animations and TypeScript support. Perfect for creating smooth, accessible animations that trigger when elements come into view.
- 🎨 CSS-only animations - No JavaScript animations, better performance
- 📱 Accessibility-first - Respects
prefers-reduced-motion - 🔧 TypeScript support - Full type safety and autocomplete
- ⚡ Framework agnostic - Works with vanilla JS, Svelte, React, and more
- 🎯 Intersection Observer - Efficient viewport detection
- 🎪 Rich presets - Pre-built animation configurations
- 🧩 Composable - Mix and match animation properties
- 📦 Tree-shakeable - Import only what you need
npm install css-motion@import 'css-motion/styles';import { initObserver } from 'css-motion';
initObserver();With Svelte:
<script>
import { animate } from 'css-motion/svelte';
import { presets } from 'css-motion';
</script>
<div {...animate.onView(presets.fadeIn(), { class: 'card' })}>Animated content</div>With React:
import { AnimateOnView } from 'css-motion/react';
import { presets } from 'css-motion';
<AnimateOnView animation={presets.fadeIn()} className="card">
Animated content
</AnimateOnView>;With Vanilla JavaScript:
import { initObserver, presets, getVarsStyle } from 'css-motion';
initObserver();
const element = document.querySelector('.my-element');
element.style.cssText += getVarsStyle(presets.slideUp());
element.classList.add('css-motion', 'css-motion--view');Initializes the global Intersection Observer for view-triggered animations. Call this once in your app:
import { initObserver } from 'css-motion';
initObserver();Generates CSS custom properties for animations:
import { getVarsStyle, presets } from 'css-motion';
const styles = getVarsStyle(presets.fadeIn());
element.style.cssText += styles;All presets accept optional parameters for customization:
import { presets } from 'css-motion';
// Fade in (delay, duration)
presets.fadeIn(0.1, 0.5);
// Slide up (delay, distance, duration)
presets.slideUp(0.1, '20px', 0.5);
// Scale in (delay, scale, duration)
presets.scaleIn(0.1, 0.9, 0.5);
// Dramatic effect (delay, duration)
presets.dramatic(0.2, 0.8);
// Also available: slideDown, slideLeft, slideRight, blurUp, scaleOut, rotateIn, bounceIn, zoomInimport { mergeConfigs, presets } from 'css-motion';
const combined = mergeConfigs(presets.fadeIn(), presets.slideUp(), { duration: 0.8 });import { stagger, presets, mergeConfigs } from 'css-motion';
items.forEach((item, index) => {
const animation = mergeConfigs(
presets.fadeIn(),
stagger(index, 0.1) // 0.1s delay between each item
);
});import { initObserver, presets, getVarsStyle } from 'css-motion';
// Initialize once
initObserver();
// Apply animations to elements
const elements = document.querySelectorAll('.animate-on-view');
elements.forEach((element, index) => {
const animation = presets.fadeIn();
element.style.cssText += getVarsStyle(animation);
element.classList.add('css-motion', 'css-motion--view');
});Use the animate helper which returns attributes to spread on elements:
<script>
import { animate } from 'css-motion/svelte';
import { presets } from 'css-motion';
</script>
<!-- Load animation - triggers immediately -->
<div {...animate.onLoad(presets.fadeIn(), { class: 'card' })}>
This animates immediately when loaded
</div>
<!-- View animation - triggers when in viewport -->
<div {...animate.onView(presets.slideUp(), { class: 'card' })}>
This animates when it comes into view
</div>
<!-- With custom observer options -->
<div
{...animate.onView(presets.rotateIn(), {
class: 'card',
observerOptions: { threshold: 0.5, once: false },
})}
>
Custom observer configuration
</div>You can use either components or hooks:
import { AnimateOnView, AnimateOnLoad, useAnimateOnView } from 'css-motion/react';
import { presets } from 'css-motion';
// Using components (recommended)
function MyComponent() {
return (
<>
<AnimateOnLoad animation={presets.fadeIn()} className="card">
This animates immediately when loaded
</AnimateOnLoad>
<AnimateOnView animation={presets.slideUp()} className="card">
This animates when it comes into view
</AnimateOnView>
</>
);
}
// Using hooks for more control
function MyComponent() {
const { ref, style, className } = useAnimateOnView(presets.rotateIn(), {
threshold: 0.5,
});
return (
<div ref={ref} style={style} className={className}>
Custom view animation
</div>
);
}interface AnimationConfig {
duration?: number; // Animation duration in seconds
delay?: number; // Animation delay in seconds
timing?: TimingFunction; // CSS timing function
translateX?: CSSLength; // Initial X translation
translateY?: CSSLength; // Initial Y translation
scale?: number; // Initial scale
rotate?: CSSAngle; // Initial rotation
blur?: CSSLength; // Initial blur
opacityStart?: number; // Starting opacity (0-1)
opacityEnd?: number; // Ending opacity (0-1)
}
interface IntersectionObserverOptions {
threshold?: number; // Intersection threshold (0-1)
rootMargin?: string; // Root margin for intersection
once?: boolean; // Trigger only once
}
interface AnimationResult {
style: string; // CSS custom properties string
class: string; // CSS class name to apply
}.css-motion- Base class with common animation properties.css-motion--load- Class for immediate animations (uses@starting-style).css-motion--view- Class for viewport-triggered animations.in-view- Automatically added when element enters viewport
All animations are controlled via CSS custom properties:
--anim-duration- Animation duration--anim-delay- Animation delay--anim-timing- Timing function--anim-translate-x- X translation--anim-translate-y- Y translation--anim-scale- Scale factor--anim-rotate- Rotation angle--anim-blur- Blur amount--anim-opacity-start- Starting opacity--anim-opacity-end- Ending opacity
- Modern browsers with CSS
@starting-stylesupport - Intersection Observer API support
- Graceful degradation for older browsers
- CSS-only animations for optimal performance
- Intersection Observer for efficient viewport detection
- Respects
prefers-reduced-motionfor accessibility - Minimal JavaScript footprint
- Fork the repository
- Create your feature branch (
git checkout -b feature/amazing-feature) - Commit your changes (
git commit -m 'Add some amazing feature') - Push to the branch (
git push origin feature/amazing-feature) - Open a Pull Request
MIT © Emanuele Pavanello