Lightweight Animation Library for Modern Web – CSS Motion

Category: Animation , Javascript | November 11, 2025
Authorepavanello
Last UpdateNovember 11, 2025
LicenseMIT
Views59 views
Lightweight Animation Library for Modern Web – CSS Motion

CSS Motion is a lightweight, framework-agnostic animation library that creates smooth, performant animations triggered by viewport visibility.

It works with vanilla JavaScript, React, Svelte, and other frameworks through a CSS-only animation approach that respects user preferences for reduced motion.

Features:

  • CSS-Only Animations: Animations run entirely in CSS for optimal performance and smooth frame rates.
  • Intersection Observer: Uses native browser APIs for efficient viewport detection without polling.
  • Rich Animation Presets: Ships with a set of pre-configured animations, including fade, slide, scale, blur, and rotation effects.
  • Tree-Shakeable Architecture: Import only the functions you need to minimize bundle size.
  • Composable Configuration: Mix multiple animation effects and timing functions through the merge utility.

See It In Action:

Use Cases:

  • Scroll-triggered content reveals: Animate sections, cards, or text as users scroll down the page.
  • Staggered list animations: Create cascading effects for product grids, feature lists, or timeline elements with built-in stagger utilities.
  • Performance-critical animations: Replace heavy JavaScript animation libraries with lightweight CSS transitions for better frame rates.

How To Use It:

1. Install & import the library into your project.

# NPM
$ npm install css-motion
@import 'css-motion/styles';
import { initObserver, presets, getVarsStyle } from 'css-motion';

2. You can also load the library from a CDN:

import { initObserver, presets, getVarsStyle } from 'https://cdn.jsdelivr.net/npm/css-motion/dist/index.esm.js';

3. Use the getVarsStyle function to generate the necessary CSS custom properties and then add the required classes to your element.

// Initialize once
initObserver();
const element = document.querySelector('.my-element');
// Get animation styles from a preset
const animation = presets.slideUp(0.1, '20px', 0.5); // (delay, distance, duration)
element.style.cssText += getVarsStyle(animation);
// Add classes to enable the animation
element.classList.add('css-motion', 'css-motion--view');

4. For multiple elements with staggered timing:

const items = document.querySelectorAll('.animate-item');
items.forEach((item, index) => {
  const animation = mergeConfigs(
    presets.fadeIn(),
    stagger(index, 0.1)
  );
  item.style.cssText += getVarsStyle(animation);
  item.classList.add('css-motion', 'css-motion--view');
});

5. The React adapter provides both component and hook APIs. Components handle the ref attachment and style application automatically:

import { AnimateOnView, AnimateOnLoad } from 'css-motion/react';
import { presets } from 'css-motion';
function MyComponent() {
  return (
    <>
      <AnimateOnLoad animation={presets.fadeIn()} className="card">
        Animates immediately on mount
      </AnimateOnLoad>
      <AnimateOnView animation={presets.slideUp()} className="card">
        Animates when scrolled into view
      </AnimateOnView>
    </>
  );
}

For cases requiring more control, use the hook API:

import { useAnimateOnView } from 'css-motion/react';
import { presets } from 'css-motion';
function MyComponent() {
  const { ref, style, className } = useAnimateOnView(presets.rotateIn(), {
    threshold: 0.5,
  });
  return (
    <div ref={ref} style={style} className={className}>
      Custom animation with hook API
    </div>
  );
}

6. The Svelte package provides a convenient animate action. You can use animate.onView to trigger the animation when the element enters the viewport or animate.onLoad for an immediate animation.

<script>
  import { animate } from 'css-motion/svelte';
  import { presets } from 'css-motion';
</script>
<!-- Animates when it enters the viewport -->
<div {...animate.onView(presets.fadeIn(), { class: 'card' })}>
  This animates into view.
</div>
<!-- Animates immediately on component load -->
<div {...animate.onLoad(presets.slideUp(), { class: 'card' })}>
  This animates on load.
</div>

7. The library includes 12 preset animations. Each preset accepts optional parameters for customization:

  • fadeIn(delay?, duration?): Simple opacity transition from zero to one.
  • slideUp(delay?, distance?, duration?): Combines fade with vertical translation from bottom.
  • slideDown(delay?, distance?, duration?): Combines fade with vertical translation from top.
  • slideLeft(delay?, distance?, duration?): Combines fade with horizontal translation from left.
  • slideRight(delay?, distance?, duration?): Combines fade with horizontal translation from right.
  • blurUp(delay?, blur?, distance?, duration?): Adds blur filter to vertical slide animation.
  • scaleIn(delay?, scale?, duration?): Combines fade with scale transformation starting below one.
  • scaleOut(delay?, scale?, duration?): Combines fade with scale transformation starting above one.
  • rotateIn(delay?, rotate?, scale?, duration?): Combines fade, rotation, and scale effects.
  • dramatic(delay?, duration?): Complex preset combining blur, slide, scale with custom cubic-bezier timing.
  • bounceIn(delay?, duration?): Scale effect with bounce timing function.
  • zoomIn(delay?, scale?, duration?): Combines scale and blur for zoom effect.
presets.fadeIn(0.1, 0.5);  // 0.1s delay, 0.5s duration
presets.slideUp(0.1, '20px', 0.5);  // Custom distance
presets.scaleIn(0.1, 0.9, 0.5);  // Custom scale factor

8. API methods:

  • initObserver(): Initializes the global Intersection Observer instance. Call once during application startup. Required for vanilla JavaScript implementations but handled automatically in React and Svelte adapters.
  • getVarsStyle(config): Generates a CSS string containing custom property declarations from an animation configuration object. Append this string to element inline styles.
  • mergeConfigs(…configs): Combines multiple animation configuration objects into a single config. Later arguments override earlier ones for conflicting properties.
  • stagger(index, delay): Generates a configuration object with a delay calculated from an index and base delay value. Useful for creating sequential animation effects.

9. Configure animations through the AnimationConfig interface:

PropertyTypeDescription
durationnumberAnimation duration in seconds
delaynumberAnimation delay in seconds
timingTimingFunctionCSS timing function string
translateXCSSLengthInitial X-axis translation
translateYCSSLengthInitial Y-axis translation
scalenumberInitial scale factor
rotateCSSAngleInitial rotation angle
blurCSSLengthInitial blur amount
opacityStartnumberStarting opacity value (0-1)
opacityEndnumberEnding opacity value (0-1)
const customAnimation = {
  duration: 0.8,
  delay: 0.2,
  timing: 'cubic-bezier(0.34, 1.56, 0.64, 1)',
  translateY: '30px',
  scale: 0.95,
  opacityStart: 0,
};

10. Control viewport detection behavior through IntersectionObserverOptions:

PropertyTypeDescription
thresholdnumberPercentage of element visibility required to trigger (0-1)
rootMarginstringMargin around viewport for early/late triggering
oncebooleanWhether animation triggers only once or repeats
<AnimateOnView 
  animation={presets.fadeIn()}
  observerOptions={{ threshold: 0.5, rootMargin: '50px', once: true }}
>
  Content
</AnimateOnView>

11. Animations are controlled through CSS variables that map to configuration properties:

VariablePurpose
–anim-durationControls animation duration
–anim-delayControls animation delay
–anim-timingSets timing function
–anim-translate-xSets initial X translation
–anim-translate-ySets initial Y translation
–anim-scaleSets initial scale factor
–anim-rotateSets initial rotation
–anim-blurSets initial blur amount
–anim-opacity-startSets starting opacity
–anim-opacity-endSets ending opacity

12. The library applies specific classes to elements:

  • .css-motion: Base class containing common animation properties and transition declarations.
  • .css-motion–load: Applied to elements that animate immediately using the @starting-style CSS feature.
  • .css-motion–view: Applied to elements that animate on viewport intersection.
  • .in-view: Automatically added by the Intersection Observer when an element enters the viewport threshold.

Alternatives:

FAQs:

Q: Can I create custom animations beyond the provided presets?
A: Build custom animations by passing configuration objects directly to animation functions. Combine multiple effects using mergeConfigs(). For example, create a custom diagonal slide by merging translateX and translateY properties with a fade effect. The configuration interface accepts any valid CSS values for transforms, timing functions, and other properties.

Q: Why aren’t my animations triggering when elements enter the viewport?
A: Check that you’ve imported the CSS stylesheet and, for vanilla JavaScript, called initObserver(). Verify elements have both the css-motion and css-motion--view classes applied. The element must also have the inline styles from getVarsStyle() applied.

Q: How do I implement animations that repeat each time an element enters the viewport?
A: Set the once option to false in the observer options. The default behavior triggers animations once, but passing { once: false } in the observerOptions parameter makes the Intersection Observer continuously monitor the element. The animation will play each time the element crosses the visibility threshold.

You Might Be Interested In:


Leave a Reply