motion

Expert guidelines for building performant animations with Motion (formerly Motion One) vanilla JavaScript animation library

INSTALLATION
npx skills add https://github.com/mindrally/skills --skill motion
Run in your project or agent environment. Adjust flags if your CLI version differs.

SKILL.md

Motion Animation Guidelines

You are an expert in Motion (motion.dev), JavaScript, TypeScript, and web animation performance. Follow these guidelines when creating animations.

Core Principles

About Motion

  • Motion is the JavaScript animation library from the creator of Framer Motion
  • Use motion for vanilla JavaScript/TypeScript projects
  • Use motion/react for React projects (see framer-motion skill)
  • Designed for high performance with minimal bundle size

Installation

npm install motion

Basic Import

import { animate, scroll, inView, timeline } from "motion";

Basic Animations

Simple Animation

import { animate } from "motion";

// Animate a single element

animate(".element", { x: 100, opacity: 1 }, { duration: 0.5 });

// Animate with options

animate(

  ".element",

  { transform: "translateX(100px)" },

  {

    duration: 0.8,

    easing: "ease-out"

  }

);

Keyframes

animate(

  ".element",

  {

    x: [0, 100, 50],      // Keyframe values

    opacity: [0, 1, 0.5]

  },

  { duration: 1 }

);

Performance Optimization

Animate Transform Properties

// Best performance - GPU accelerated

animate(".element", {

  x: 100,           // translateX

  y: 50,            // translateY

  scale: 1.2,       // scale

  rotate: 45,       // rotate

  opacity: 0.5      // opacity

});

// Avoid when possible - triggers layout

animate(".element", {

  width: 200,       // Causes layout recalculation

  height: 150,      // Causes layout recalculation

  top: 50,          // Causes layout recalculation

  left: 100         // Causes layout recalculation

});

Use will-change

// Add will-change for transform animations

const element = document.querySelector(".element");

element.style.willChange = "transform";

animate(element, { x: 100 }, {

  onComplete: () => {

    element.style.willChange = "auto"; // Remove after animation

  }

});

Hardware Acceleration

Motion automatically uses hardware-accelerated properties when possible. For best performance:

  • Prefer x, y over left, top
  • Prefer scale over width, height
  • Use opacity for fade effects
  • Use rotate over transform: rotate()

Timeline Animations

Create Timelines

import { timeline } from "motion";

const sequence = [

  [".header", { y: ["-100%", 0], opacity: [0, 1] }],

  [".content", { y: [50, 0], opacity: [0, 1] }, { at: "-0.3" }],

  [".footer", { y: [50, 0], opacity: [0, 1] }, { at: "-0.3" }]

];

const controls = timeline(sequence, {

  duration: 0.8,

  defaultOptions: { easing: "ease-out" }

});

Timeline Controls

const controls = timeline(sequence);

controls.play();

controls.pause();

controls.reverse();

controls.stop();

controls.finish();

// Seek to specific time

controls.currentTime = 0.5;

Scroll Animations

Basic Scroll Animation

import { scroll, animate } from "motion";

scroll(

  animate(".progress-bar", { scaleX: [0, 1] }),

  { target: document.querySelector("article") }

);

Scroll-Linked Animation

scroll(({ y }) => {

  // y.progress is 0 to 1

  animate(".element", {

    opacity: y.progress,

    y: y.progress * 100

  }, { duration: 0 });

});

Scroll with Container

scroll(

  animate(".parallax", { y: [0, -100] }),

  {

    target: document.querySelector(".section"),

    offset: ["start end", "end start"]

  }

);

In-View Animations

Trigger on Visibility

import { inView, animate } from "motion";

inView(".card", (info) => {

  animate(info.target, { opacity: 1, y: 0 }, { duration: 0.5 });

  // Return cleanup function

  return () => {

    animate(info.target, { opacity: 0, y: 20 }, { duration: 0.2 });

  };

});

With Options

inView(

  ".element",

  (info) => {

    animate(info.target, { scale: [0.8, 1], opacity: [0, 1] });

  },

  {

    margin: "-100px",  // Trigger 100px before entering viewport

    amount: 0.5        // Trigger when 50% visible

  }

);

Stagger Animations

Stagger Multiple Elements

import { stagger, animate } from "motion";

animate(

  ".list-item",

  { opacity: [0, 1], y: [20, 0] },

  { delay: stagger(0.1) }

);

Stagger from Center

animate(

  ".grid-item",

  { scale: [0, 1] },

  { delay: stagger(0.05, { from: "center" }) }

);

Stagger with Easing

animate(

  ".item",

  { x: ["-100%", 0] },

  {

    delay: stagger(0.1, {

      easing: "ease-out",

      start: 0.2

    })

  }

);

Spring Animations

Use Springs for Natural Motion

animate(

  ".element",

  { scale: 1.2 },

  {

    easing: "spring",

    // or with custom spring settings

    easing: [0.34, 1.56, 0.64, 1] // Custom bezier curve

  }

);

Spring Options

animate(".element", { x: 100 }, {

  type: "spring",

  stiffness: 300,

  damping: 30

});

Easing Functions

Built-in Easings

// Common easing values

animate(".element", { x: 100 }, { easing: "ease" });

animate(".element", { x: 100 }, { easing: "ease-in" });

animate(".element", { x: 100 }, { easing: "ease-out" });

animate(".element", { x: 100 }, { easing: "ease-in-out" });

animate(".element", { x: 100 }, { easing: "linear" });

// Cubic bezier

animate(".element", { x: 100 }, {

  easing: [0.25, 0.1, 0.25, 1]

});

Animation Controls

Control Playback

const controls = animate(".element", { x: 100 }, { duration: 1 });

// Control methods

controls.play();

controls.pause();

controls.stop();

controls.finish();

controls.reverse();

// Get/set time

controls.currentTime = 0.5;

console.log(controls.duration);

// Cancel animation

controls.cancel();

Animation Events

const controls = animate(

  ".element",

  { x: 100 },

  {

    duration: 1,

    onComplete: () => console.log("Done!")

  }

);

// Promise-based

controls.finished.then(() => {

  console.log("Animation finished");

});

Accessibility

Respect Reduced Motion

const prefersReducedMotion = window.matchMedia(

  "(prefers-reduced-motion: reduce)"

).matches;

animate(

  ".element",

  { x: 100, opacity: 1 },

  {

    duration: prefersReducedMotion ? 0 : 0.5,

    easing: prefersReducedMotion ? "linear" : "ease-out"

  }

);

Create Accessible Wrapper

function safeAnimate(element, keyframes, options = {}) {

  const reducedMotion = window.matchMedia(

    "(prefers-reduced-motion: reduce)"

  ).matches;

  return animate(element, keyframes, {

    ...options,

    duration: reducedMotion ? 0 : (options.duration ?? 0.3)

  });

}

Integration with Frameworks

Vanilla JavaScript

document.addEventListener("DOMContentLoaded", () => {

  animate(".hero", { opacity: [0, 1], y: [30, 0] });

});

With Event Listeners

const button = document.querySelector(".button");

button.addEventListener("mouseenter", () => {

  animate(button, { scale: 1.05 }, { duration: 0.2 });

});

button.addEventListener("mouseleave", () => {

  animate(button, { scale: 1 }, { duration: 0.2 });

});

Cleanup

Cancel Animations

const controls = animate(".element", { x: 100 });

// Later, cancel it

controls.cancel();

Cleanup Pattern

class AnimatedComponent {

  constructor(element) {

    this.element = element;

    this.animations = [];

  }

  animate(keyframes, options) {

    const controls = animate(this.element, keyframes, options);

    this.animations.push(controls);

    return controls;

  }

  destroy() {

    this.animations.forEach(anim => anim.cancel());

    this.animations = [];

  }

}

Best Practices Summary

  • Use transform properties (x, y, scale, rotate) for best performance
  • Add will-change before complex animations, remove after
  • Use timeline for sequenced animations
  • Use scroll() for scroll-linked effects
  • Use inView() for viewport-triggered animations
  • Use stagger() for animating multiple elements
  • Prefer springs for interactive/gesture animations
  • Always respect reduced motion preferences
  • Cancel animations when no longer needed
  • Test performance on actual devices
BrowserAct

Let your agent run on any real-world website

Bypass CAPTCHA & anti-bot for free. Start local, scale to cloud.

Explore BrowserAct Skills →

Stop writing automation&scrapers

Install the CLI. Run your first Skill in 30 seconds. Scale when you're ready.

Start free
free · no credit card