Skip to content

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.

License

Notifications You must be signed in to change notification settings

epavanello/css-motion

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

26 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

css-motion

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.

Features

  • 🎨 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

Installation

npm install css-motion

Quick Start

1. Import the CSS

@import 'css-motion/styles';

2. Initialize the observer (for vanilla JavaScript)

import { initObserver } from 'css-motion';

initObserver();

3. Use the library with your framework

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');

Usage

Core API

initObserver()

Initializes the global Intersection Observer for view-triggered animations. Call this once in your app:

import { initObserver } from 'css-motion';

initObserver();

getVarsStyle(config)

Generates CSS custom properties for animations:

import { getVarsStyle, presets } from 'css-motion';

const styles = getVarsStyle(presets.fadeIn());
element.style.cssText += styles;

Animation Presets

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, zoomIn

Combining Animations

import { mergeConfigs, presets } from 'css-motion';

const combined = mergeConfigs(presets.fadeIn(), presets.slideUp(), { duration: 0.8 });

Staggered Animations

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
  );
});

Vanilla JavaScript Integration

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');
});

Svelte Integration

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>

React Integration

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>
  );
}

API Reference

Core Types

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 Classes

  • .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

CSS Custom Properties

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

Browser Support

  • Modern browsers with CSS @starting-style support
  • Intersection Observer API support
  • Graceful degradation for older browsers

Performance

  • CSS-only animations for optimal performance
  • Intersection Observer for efficient viewport detection
  • Respects prefers-reduced-motion for accessibility
  • Minimal JavaScript footprint

Contributing

  1. Fork the repository
  2. Create your feature branch (git checkout -b feature/amazing-feature)
  3. Commit your changes (git commit -m 'Add some amazing feature')
  4. Push to the branch (git push origin feature/amazing-feature)
  5. Open a Pull Request

License

MIT © Emanuele Pavanello

About

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.

Resources

License

Stars

Watchers

Forks