Skip to content
Development
Skill

/motion-ui

Production-ready UI motion system for React/Next.js. Use when implementing animations, transitions, or motion patterns.

From plugin
ecc
239k200 skills72 agents109 commands7 hooks
+1
Install
$ npx -y skills add affaan-m/ECC --skill motion-ui --agent claude-code

How it fires

How this skill gets triggered: by you, by Claude, or both.

  • Fires itselfAuto-invocation. Claude auto-loads it when your prompt matches the work.Auto-invocation is when the right skill fires by itself at the right moment, driven by a FLOW.md router and a hook, instead of you invoking it by name. It is the difference between a skill being installed and a skill actually getting used.Read the full definition →
  • You can call itInvoke it directly when you want it.
  • Slash command/motion-ui

Context preview

The summary Claude sees to decide when to auto-load this skill.

Production-ready UI motion system for React/Next.js. Use when implementing animations, transitions, or motion patterns.

SKILL.md

motion-ui.SKILL.md
name: motion-ui
description: "Production-ready UI motion system for React/Next.js. Use when implementing animations, transitions, or motion patterns."
metadata:
  origin: ECC

Motion System v4.2

Production-ready UI motion system for React / Next.js.

Focused on **performance, accessibility, and usability** — not decoration.

When to Use

Use this motion system when motion:

  • Guides attention (e.g., onboarding, key actions)
  • Communicates state (loading, success, error, transitions)
  • Preserves spatial continuity (layout changes, navigation)

Appropriate Scenarios

  • Interactive components (buttons, modals, menus)
  • State transitions (loading → loaded, open → closed)
  • Navigation and layout continuity (shared elements, crossfade)

Considerations

  • **Accessibility**: Always support reduced motion
  • **Device adaptation**: Adjust for low-end devices
  • **Performance trade-offs**: Prefer responsiveness over visual smoothness

Avoid Using Motion When

  • It is purely decorative
  • It reduces usability or clarity
  • It impacts performance negatively

---

How It Works

Core Principle

Motion must:

  • Guide attention
  • Communicate state
  • Preserve spatial continuity

If it does none → remove it.

---

Installation

npm install motion

---

Version

  • `motion/react` - default for current Motion for React projects (package: `motion`)
  • `framer-motion` - legacy import path for projects that still depend on Framer Motion

**Do not mix.** Mixing causes conflicting internal schedulers and broken `AnimatePresence` contexts — components from one package will not coordinate exit animations with components from the other.

To check which version your project uses:

cat package.json | grep -E '"motion"|"framer-motion"'

Always import from one source consistently:

// Correct (modern)
import { motion, AnimatePresence } from "motion/react"

// Correct (legacy)
import { motion, AnimatePresence } from "framer-motion"

// Never mix both in the same project

---

Motion Tokens

// motionTokens.ts
export const motionTokens = {
  duration: {
    fast: 0.18,
    normal: 0.35,
    slow: 0.6
  },
  // Use these as the `ease` value inside a `transition` object:
  // transition={{ duration: motionTokens.duration.normal, ease: motionTokens.easing.smooth }}
  easing: {
    smooth: [0.22, 1, 0.36, 1] as [number, number, number, number],
    sharp:  [0.4,  0, 0.2, 1] as [number, number, number, number]
  },
  distance: {
    sm: 8,
    md: 16,
    lg: 24
  }
}

Usage example:

import { motionTokens } from "@/lib/motionTokens"

<motion.div
  initial={{ opacity: 0, y: motionTokens.distance.md }}
  animate={{ opacity: 1, y: 0 }}
  transition={{
    duration: motionTokens.duration.normal,
    ease: motionTokens.easing.smooth
  }}
/>

---

Performance Rules

**Safe**

  • transform
  • opacity

**Avoid**

  • width / height
  • top / left

Rule: responsiveness > smoothness

---

Device Adaptation

The heuristic combines CPU core count **and** available memory for a more reliable signal. `deviceMemory` is available on Chrome/Android; the fallback covers Safari and Firefox.

const isLowEnd =
  typeof navigator !== "undefined" && (
    // Low memory (Chrome/Android only; undefined elsewhere → treat as capable)
    (navigator.deviceMemory !== undefined && navigator.deviceMemory <= 2) ||
    // Few cores AND no memory API (covers Safari/Firefox on weak hardware)
    (navigator.deviceMemory === undefined && navigator.hardwareConcurrency <= 4)
  )

const duration = isLowEnd ? 0.2 : 0.4

---

Accessibility

JS (useReducedMotion)

import { motion, useReducedMotion } from "motion/react"

export function FadeIn() {
  const reduce = useReducedMotion()

  return (
    <motion.div
      initial={{ opacity: 0, y: reduce ? 0 : 24 }}
      animate={{ opacity: 1, y: 0 }}
    />
  )
}

CSS

@media (prefers-reduced-motion: reduce) {
  .motion-safe-transition {
    transition: opacity 0.2s;
  }

  .motion-reduce-transform {
    transform: none !important;
  }
}

Tailwind

<div class="motion-safe:animate-fade motion-reduce:opacity-100"></div>

---

Architecture & Patterns

Core Patterns

| Scenario | Pattern | |---|---| | Hover feedback | `whileHover` | | Tap / press feedback | `whileTap` | | Reveal on scroll | `whileInView` | | Scroll-linked value | `useScroll` + `useTransform` | | Conditional mount/unmount | `AnimatePresence` | | Small layout shifts (single element, < ~300px change) | `layout` prop | | Large layout shifts or full-page reflows | Avoid `layout`; use CSS transitions or page-level routing instead | | Complex, imperative sequences | `useAnimate` |

> **Why avoid `layout` on large containers?** Framer's layout animation uses `transform` to reconcile positions, but on elements that span the full viewport or trigger deep reflow, the measurement cost causes visible jank and CLS. Prefer CSS Grid/Flexbox transitions or coordinate with `layoutId` on specific child elements only.

Layout & Transitions

  • Shared element transitions → `layoutId` (must be unique per mounted instance)
  • Enter / exit transitions → `AnimatePresence` (see `mode` guidance below)

AnimatePresence `mode`

Always specify `mode` explicitly — the default (`"sync"`) runs enter and exit simultaneously, which causes visual overlap in most UI patterns.

| `mode` | When to use | |---|---| | `"wait"` | Exit completes before enter starts. Use for **modals, toasts, page transitions**. | | `"sync"` (default) | Enter and exit overlap. Use only when overlap is intentional (e.g., crossfade carousels). | | `"popLayout"` | Exiting element is popped out of flow immediately; remaining items animate to fill. Use for **lists, tabs, dismissible cards**. |

// Modal — always use "wait"
<AnimatePresence mode="wait">
  {open && <Modal key="modal" />}
</AnimatePresence>

// Dismissible list item — use "popLayout
Read more
Ships withecc

Your agent can write code, but ECC gives it a coordinated engineering system and toolbox: it plans before it builds, verifies changes with tests, reviews its own work from a fresh context, remembers what matters, and turns repeated wins into reusable skills

Get the whole plugin