Claude-skill-registry animation
Expert guidance for creating premium, performant animations in React using Motion (motion.dev). Covers all animation types, best practices, accessibility, and performance optimization.
git clone https://github.com/majiayu000/claude-skill-registry
T=$(mktemp -d) && git clone --depth=1 https://github.com/majiayu000/claude-skill-registry "$T" && mkdir -p ~/.claude/skills && cp -r "$T/skills/data/animation" ~/.claude/skills/majiayu000-claude-skill-registry-animation && rm -rf "$T"
skills/data/animation/SKILL.mdAnimation Skill
You are an expert in creating world-class, premium animations for React applications using Motion (motion.dev), the modern animation library for React.
Installation
npm install motion
Import from
"motion/react":
import { motion } from "motion/react";
Core Concepts
1. The <motion />
Component
<motion />The foundation of all animations in Motion. It's a React component that wraps any HTML or SVG element and supercharges it with animation capabilities.
Basic Usage:
<motion.div animate={{ x: 100 }} /> <motion.button whileHover={{ scale: 1.1 }} /> <motion.svg whileTap={{ rotate: 90 }} />
Key Props:
: Starting visual state (can be an object, variant name, orinitial
to disable enter animation)false
: Target state to animate toanimate
: State to animate to when removed from DOM (requiresexit
)<AnimatePresence>
: Customize animation timing and easingtransition
: Reusable animation statesvariants
: React style prop with support for MotionValuesstyle
Animation Types
2. Enter Animations
Components automatically animate to
animate values when they mount.
<motion.div initial={{ opacity: 0, y: 50 }} animate={{ opacity: 1, y: 0 }} />
Disable enter animation:
<motion.div initial={false} animate={{ opacity: 1 }} />
3. Gesture Animations
Motion provides declarative gesture handlers that feel better than CSS or plain JavaScript events.
Hover
<motion.button whileHover={{ scale: 1.1, backgroundColor: "#ff0000" }} transition={{ duration: 0.2 }} />
Tap/Press
<motion.button whileTap={{ scale: 0.95 }} onTap={() => console.log("Tapped!")} />
Focus
<motion.input whileFocus={{ borderColor: "#0099ff" }} />
Drag
<motion.div drag dragConstraints={{ left: -100, right: 100, top: -100, bottom: 100 }} whileDrag={{ scale: 1.1 }} />
Drag Options:
: Drag in all directionsdrag={true}
: Horizontal onlydrag="x"
: Vertical onlydrag="y"
: Limits (object or ref to container)dragConstraints
: Elasticity when out of bounds (0-1, default: 0.5)dragElastic
: Enable momentum on release (default: true)dragMomentum
4. Scroll Animations
Scroll-Triggered (whileInView)
Animate when element enters viewport:
<motion.div initial={{ opacity: 0, y: 100 }} whileInView={{ opacity: 1, y: 0 }} viewport={{ once: true, amount: 0.5 }} />
viewport options:
: Only trigger once (default: false)once
: How much of element must be visible ("some", "all", or 0-1)amount
: Offset from viewport edgesmargin
: Custom scroll containerroot
Scroll-Linked (useScroll)
Link animations directly to scroll position:
const { scrollYProgress } = useScroll(); return <motion.div style={{ scaleX: scrollYProgress }} />;
useScroll returns:
,scrollX
: Scroll offset in pixelsscrollY
,scrollXProgress
: Scroll progress (0-1)scrollYProgress
Advanced scroll tracking:
const { scrollYProgress } = useScroll({ target: ref, // Element to track offset: ["start end", "end start"], // When to start/end });
5. Layout Animations
Motion uses FLIP (First, Last, Invert, Play) to animate layout changes using performant transforms.
Simple Layout Animation
<motion.div layout />
Shared Element Transitions
{ isExpanded ? <motion.div layoutId="card" /> : <motion.div layoutId="card" />; }
Layout Props:
: Animate size and position changeslayout
: Shared element transitions between componentslayoutId
: Force recalculation on value changelayoutDependency
: Animate within scrollable containerslayoutScroll
Performance Note: Layout animations run at 60fps by animating transforms, not layout properties.
6. Exit Animations
Wrap components with
<AnimatePresence> to enable exit animations:
<AnimatePresence mode="wait"> {isVisible && ( <motion.div key="modal" initial={{ opacity: 0 }} animate={{ opacity: 1 }} exit={{ opacity: 0 }} /> )} </AnimatePresence>
AnimatePresence Props:
: "sync" (default), "wait", "popLayout"mode
: Disable initial animations (default: true)initial
: Callback when all exit animations completeonExitComplete
7. SVG Animations
Motion supports SVG-specific animations:
// Line drawing <motion.path initial={{ pathLength: 0 }} animate={{ pathLength: 1 }} /> // Morphing (same number of points) <motion.path animate={{ d: "M10,10 L20,20 ..." }} /> // Attributes <motion.circle animate={{ cx: 50, r: 20, fill: "#ff0000" }} />
Advanced Features
8. Variants
Reusable animation states with propagation and orchestration:
const variants = { hidden: { opacity: 0, y: 20 }, visible: { opacity: 1, y: 0, transition: { delay: 0.2, when: "beforeChildren", staggerChildren: 0.1, }, }, }; <motion.ul variants={variants} initial="hidden" animate="visible"> <motion.li variants={variants} /> <motion.li variants={variants} /> </motion.ul>;
Variant Orchestration:
: "beforeChildren", "afterChildren"when
: Delay between child animationsstaggerChildren
: Delay before first childdelayChildren
: 1 (forward) or -1 (backward)staggerDirection
Dynamic Variants:
const variants = { visible: (i) => ({ opacity: 1, transition: { delay: i * 0.1 }, }), }; <motion.div custom={index} variants={variants} />;
9. Transitions
Configure animation timing and behavior:
Tween (Time-based)
<motion.div animate={{ x: 100 }} transition={{ duration: 0.5, ease: "easeInOut", // or [.17,.67,.83,.67] for cubic-bezier }} />
Easing options: "linear", "easeIn", "easeOut", "easeInOut", "circIn", "circOut", "circInOut", "backIn", "backOut", "backInOut", "anticipate"
Spring (Physics-based)
<motion.div animate={{ x: 100 }} transition={{ type: "spring", stiffness: 100, damping: 10, mass: 1, }} />
Spring Presets:
: Natural feel (default)bounce: 0.25
: No bouncebounce: 0
: Very bouncybounce: 0.6
Or use
duration with bounce:
transition={{ duration: 0.8, bounce: 0.3 }}
Keyframes
<motion.div animate={{ x: [0, 100, 0], backgroundColor: ["#ff0000", "#00ff00", "#0000ff"], }} transition={{ duration: 2, times: [0, 0.5, 1], // Optional: control keyframe timing ease: ["easeIn", "easeOut"], // Different easing per segment }} />
10. Motion Values
Track and compose values without triggering re-renders:
const x = useMotionValue(0); const opacity = useTransform(x, [0, 100], [1, 0]); return <motion.div style={{ x, opacity }} />;
Key Hooks:
useMotionValue
const x = useMotionValue(0); x.set(100); // Update without re-render x.get(); // Get current value
useTransform
// Map one range to another const y = useTransform(scrollY, [0, 300], [0, 100]); // Custom function const color = useTransform(x, (latest) => latest > 50 ? "#ff0000" : "#0000ff", );
useSpring
const x = useMotionValue(0); const smoothX = useSpring(x, { stiffness: 100, damping: 20, });
useScroll
const { scrollYProgress } = useScroll({ target: containerRef, offset: ["start start", "end end"], });
useInView
const ref = useRef(null); const isInView = useInView(ref, { once: true, amount: 0.5, });
useVelocity
const x = useMotionValue(0); const xVelocity = useVelocity(x);
Premium Components
11. AnimateNumber
Animate number changes with layout animations:
import { AnimateNumber } from "motion/react"; <AnimateNumber value={count} transition={{ duration: 0.5 }} />;
12. Carousel
Production-ready carousel with infinite scrolling:
import { Carousel } from "motion/react"; <Carousel.Root loop> <Carousel.Viewport> {items.map((item) => ( <Carousel.Item key={item.id}>{item.content}</Carousel.Item> ))} </Carousel.Viewport> <Carousel.Controls /> </Carousel.Root>;
13. Cursor
Custom cursor with auto-adaptation to interactive elements:
import { Cursor } from "motion/react"; <Cursor className="custom-cursor" />;
14. Ticker
Infinite scrolling marquee:
import { Ticker } from "motion/react"; <Ticker speed={50}> <div>Scrolling content...</div> </Ticker>;
15. Typewriter
Realistic typing animation:
import { Typewriter } from "motion/react"; <Typewriter text="Hello, world!" speed={50} />;
16. Reorder
Drag-to-reorder lists:
import { Reorder } from "motion/react"; <Reorder.Group values={items} onReorder={setItems}> {items.map((item) => ( <Reorder.Item key={item} value={item}> {item} </Reorder.Item> ))} </Reorder.Group>;
Performance & Optimization
17. LazyMotion
Reduce bundle size by loading features on demand:
import { LazyMotion, domAnimation } from "motion/react"; <LazyMotion features={domAnimation} strict> <App /> </LazyMotion>;
Features:
: ~30kb (gestures, drag, layout)domAnimation
: ~60kb (all features)domMax- Async loading:
features={() => import('./features')}
18. MotionConfig
Configure all child motion components:
<MotionConfig transition={{ duration: 0.3 }} reducedMotion="user" // Respect prefers-reduced-motion > <App /> </MotionConfig>
19. Performance Tips
Hardware Acceleration: Motion automatically uses transforms for layout animations (60fps).
Avoid animating:
width, height, top, left directly. Use scale and x/y instead.
Good:
<motion.div animate={{ x: 100, scale: 1.2 }} />
Bad:
<motion.div animate={{ left: 100, width: 200 }} /> // Forces layout recalc
Will-change: Motion automatically applies
will-change when needed.
Reduce render triggers: Use MotionValues to update without re-renders.
Accessibility
20. Reduced Motion
Respect user preferences:
const shouldReduceMotion = useReducedMotion(); <motion.div animate={shouldReduceMotion ? { opacity: 1 } : { opacity: 1, y: 0 }} />;
Or globally:
<MotionConfig reducedMotion="user"> <App /> </MotionConfig>
Best Practices
21. Premium Animation Guidelines
-
Natural Motion: Use spring animations for interactive elements
transition={{ type: "spring", bounce: 0.25 }} -
Micro-interactions: Add subtle hover/tap feedback
whileHover={{ scale: 1.05 }} whileTap={{ scale: 0.98 }} -
Stagger Children: Create elegant cascading effects
variants={{ visible: { transition: { staggerChildren: 0.1 } } }} -
Smooth Scroll Links: Use scroll-linked animations for parallax and progress
const { scrollYProgress } = useScroll(); -
Exit Animations: Always animate elements out, don't just remove them
<AnimatePresence mode="wait">{/* content */}</AnimatePresence> -
Layout Animations: Use
prop for seamless size/position transitionslayout<motion.div layout /> -
Performant Transforms: Use scale/translate over width/height
// Good animate={{ scale: 1.2, x: 100 }} // Bad animate={{ width: 200, left: 100 }}
Common Patterns
22. Page Transitions
<AnimatePresence mode="wait"> <motion.div key={router.pathname} initial={{ opacity: 0, x: -20 }} animate={{ opacity: 1, x: 0 }} exit={{ opacity: 0, x: 20 }} transition={{ duration: 0.3 }} > {children} </motion.div> </AnimatePresence>
23. Modal/Dialog
<AnimatePresence> {isOpen && ( <> <motion.div initial={{ opacity: 0 }} animate={{ opacity: 1 }} exit={{ opacity: 0 }} onClick={onClose} className="backdrop" /> <motion.div initial={{ opacity: 0, scale: 0.9, y: 20 }} animate={{ opacity: 1, scale: 1, y: 0 }} exit={{ opacity: 0, scale: 0.9, y: 20 }} className="modal" > {content} </motion.div> </> )} </AnimatePresence>
24. Accordion
<motion.div layout> <motion.button onClick={toggle} layout> {title} </motion.button> <AnimatePresence initial={false}> {isOpen && ( <motion.div initial={{ height: 0, opacity: 0 }} animate={{ height: "auto", opacity: 1 }} exit={{ height: 0, opacity: 0 }} > {content} </motion.div> )} </AnimatePresence> </motion.div>
25. Parallax Scroll
const { scrollYProgress } = useScroll(); const y = useTransform(scrollYProgress, [0, 1], [0, -500]); return <motion.div style={{ y }}>{content}</motion.div>;
26. Hover Cards
<motion.div whileHover={{ scale: 1.05, boxShadow: "0px 10px 30px rgba(0,0,0,0.2)", }} transition={{ type: "spring", stiffness: 300 }} > {content} </motion.div>
Troubleshooting
Common Issues
Layout animations not working:
- Ensure element has defined dimensions
- Check parent isn't
display: inline - Verify no
in CSS (conflicts with Motion)transform
Exit animations not working:
- Must be direct child of
<AnimatePresence> - Ensure unique
propkey - Check component isn't conditional before
<AnimatePresence>
Performance issues:
- Avoid animating layout properties directly
- Use
sparingly (Motion handles this)will-change - Consider
for bundle optimizationLazyMotion
SVG animations broken:
- Set
instead oflayout="position"layout={true} - Use
/attrX
for SVG positioningattrY
Resources
- Documentation: https://motion.dev/docs/react
- Examples: https://motion.dev/examples
- GitHub: https://github.com/motiondivision/motion
Summary
Motion is the most powerful animation library for React, offering:
- ✅ Declarative API with
components<motion /> - ✅ Gesture support (hover, tap, drag, focus)
- ✅ Layout animations using FLIP
- ✅ Scroll-triggered and scroll-linked animations
- ✅ Exit animations with
<AnimatePresence> - ✅ SVG animation support
- ✅ Motion values for performance
- ✅ Accessibility with reduced motion support
- ✅ Premium components (Carousel, Ticker, Typewriter, etc.)
- ✅ Tree-shakeable and optimizable with LazyMotion
Remember: Always prioritize performance by using transforms, respect user preferences with reduced motion, and create premium experiences with natural spring animations and thoughtful micro-interactions.