gpt-taste
gpt taste
animation
Expert guidance for creating premium, performant animations in React using Motion (motion.dev). Covers all animation types, best practices, accessibility, and performance optimization.
Full skill instructions
You are an expert in creating world-class, premium animations for React applications using Motion (motion.dev), the modern animation library for React.
npm install motion
Import from "motion/react":
import { motion } from "motion/react";
<motion /> ComponentThe 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:
initial: Starting visual state (can be an object, variant name, or false to disable enter animation)animate: Target state to animate toexit: State to animate to when removed from DOM (requires <AnimatePresence>)transition: Customize animation timing and easingvariants: Reusable animation statesstyle: React style prop with support for MotionValuesComponents 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 }} />
Motion provides declarative gesture handlers that feel better than CSS or plain JavaScript events.
<motion.button
whileHover={{ scale: 1.1, backgroundColor: "#ff0000" }}
transition={{ duration: 0.2 }}
/>
<motion.button
whileTap={{ scale: 0.95 }}
onTap={() => console.log("Tapped!")}
/>
<motion.input whileFocus={{ borderColor: "#0099ff" }} />
<motion.div
drag
dragConstraints={{ left: -100, right: 100, top: -100, bottom: 100 }}
whileDrag={{ scale: 1.1 }}
/>
Drag Options:
drag={true}: Drag in all directionsdrag="x": Horizontal onlydrag="y": Vertical onlydragConstraints: Limits (object or ref to container)dragElastic: Elasticity when out of bounds (0-1, default: 0.5)dragMomentum: Enable momentum on release (default: true)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:
once: Only trigger once (default: false)amount: How much of element must be visible ("some", "all", or 0-1)margin: Offset from viewport edgesroot: Custom scroll containerLink animations directly to scroll position:
const { scrollYProgress } = useScroll();
return <motion.div style={{ scaleX: scrollYProgress }} />;
useScroll returns:
scrollX, scrollY: Scroll offset in pixelsscrollXProgress, scrollYProgress: Scroll progress (0-1)Advanced scroll tracking:
const { scrollYProgress } = useScroll({
target: ref, // Element to track
offset: ["start end", "end start"], // When to start/end
});
Motion uses FLIP (First, Last, Invert, Play) to animate layout changes using performant transforms.
<motion.div layout />
{
isExpanded ? <motion.div layoutId="card" /> : <motion.div layoutId="card" />;
}
Layout Props:
layout: Animate size and position changeslayoutId: Shared element transitions between componentslayoutDependency: Force recalculation on value changelayoutScroll: Animate within scrollable containersPerformance Note: Layout animations run at 60fps by animating transforms, not layout properties.
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:
mode: "sync" (default), "wait", "popLayout"initial: Disable initial animations (default: true)onExitComplete: Callback when all exit animations completeMotion 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"
}}
/>
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:
when: "beforeChildren", "afterChildren"staggerChildren: Delay between child animationsdelayChildren: Delay before first childstaggerDirection: 1 (forward) or -1 (backward)Dynamic Variants:
const variants = {
visible: (i) => ({
opacity: 1,
transition: { delay: i * 0.1 },
}),
};
<motion.div custom={index} variants={variants} />;
Configure animation timing and behavior:
<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"
<motion.div
animate={{ x: 100 }}
transition={{
type: "spring",
stiffness: 100,
damping: 10,
mass: 1,
}}
/>
Spring Presets:
bounce: 0.25: Natural feel (default)bounce: 0: No bouncebounce: 0.6: Very bouncyOr use duration with bounce:
transition={{ duration: 0.8, bounce: 0.3 }}
<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
}}
/>
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:
const x = useMotionValue(0);
x.set(100); // Update without re-render
x.get(); // Get current value
// Map one range to another
const y = useTransform(scrollY, [0, 300], [0, 100]);
// Custom function
const color = useTransform(x, (latest) =>
latest > 50 ? "#ff0000" : "#0000ff",
);
const x = useMotionValue(0);
const smoothX = useSpring(x, {
stiffness: 100,
damping: 20,
});
const { scrollYProgress } = useScroll({
target: containerRef,
offset: ["start start", "end end"],
});
const ref = useRef(null);
const isInView = useInView(ref, {
once: true,
amount: 0.5,
});
const x = useMotionValue(0);
const xVelocity = useVelocity(x);
Animate number changes with layout animations:
import { AnimateNumber } from "motion/react";
<AnimateNumber value={count} transition={{ duration: 0.5 }} />;
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>;
Custom cursor with auto-adaptation to interactive elements:
import { Cursor } from "motion/react";
<Cursor className="custom-cursor" />;
Infinite scrolling marquee:
import { Ticker } from "motion/react";
<Ticker speed={50}>
<div>Scrolling content...</div>
</Ticker>;
Realistic typing animation:
import { Typewriter } from "motion/react";
<Typewriter text="Hello, world!" speed={50} />;
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>;
Reduce bundle size by loading features on demand:
import { LazyMotion, domAnimation } from "motion/react";
<LazyMotion features={domAnimation} strict>
<App />
</LazyMotion>;
Features:
domAnimation: ~30kb (gestures, drag, layout)domMax: ~60kb (all features)features={() => import('./features')}Configure all child motion components:
<MotionConfig
transition={{ duration: 0.3 }}
reducedMotion="user" // Respect prefers-reduced-motion
>
<App />
</MotionConfig>
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.
Respect user preferences:
const shouldReduceMotion = useReducedMotion();
<motion.div
animate={shouldReduceMotion ? { opacity: 1 } : { opacity: 1, y: 0 }}
/>;
Or globally:
<MotionConfig reducedMotion="user">
<App />
</MotionConfig>
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 layout prop for seamless size/position transitions
<motion.div layout />
Performant Transforms: Use scale/translate over width/height
// Good
animate={{ scale: 1.2, x: 100 }}
// Bad
animate={{ width: 200, left: 100 }}
<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>
<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>
<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>
const { scrollYProgress } = useScroll();
const y = useTransform(scrollYProgress, [0, 1], [0, -500]);
return <motion.div style={{ y }}>{content}</motion.div>;
<motion.div
whileHover={{
scale: 1.05,
boxShadow: "0px 10px 30px rgba(0,0,0,0.2)",
}}
transition={{ type: "spring", stiffness: 300 }}
>
{content}
</motion.div>
Layout animations not working:
display: inlinetransform in CSS (conflicts with Motion)Exit animations not working:
<AnimatePresence>key prop<AnimatePresence>Performance issues:
will-change sparingly (Motion handles this)LazyMotion for bundle optimizationSVG animations broken:
layout="position" instead of layout={true}attrX/attrY for SVG positioningMotion is the most powerful animation library for React, offering:
<motion /> components<AnimatePresence>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.
gpt taste
Premium brand-kit image generation for high-end identity systems, logo concepts, and visual-world presentations.
Premium website design-to-code skill that generates visual references first, then builds faithful frontend implementations.
Premium frontend image direction for landing pages and marketing sites, generating one distinct horizontal image per section.
Premium mobile app screen concepts and flows with clean hierarchy, strong imagery, and distinctive art direction.
Emil Kowalski's philosophy on UI polish, component design, and the invisible details that make software feel good.
Frontend design and iteration for production-grade interfaces, from concept to ship.
Reverse-engineer design tokens from public websites into starter token files for your project.
Builds an animation from scratch by making the decisions in the order that determines whether it feels right.
Create beautiful visual art in .png and .pdf documents using design philosophy. You should use this skill when the user asks to create a poster, piece of art, design, or other static piece. Create original visual designs, never copying existing artists' work to avoid copyright violations.
Applies Anthropic's official brand colors and typography to any sort of artifact that may benefit from having Anthropic's look-and-feel. Use it when brand colors or style guidelines, visual formatting, or company design standards apply.
Toolkit for styling artifacts with a theme. These artifacts can be slides, docs, reportings, HTML landing pages, etc. There are 10 pre-set themes with colors/fonts that you can apply to any artifact that has been creating, or can generate a new theme on-the-fly.
Chat-to-edit AI studio for product photos, portraits, branding, and video.
Generate your dream room in seconds.
Redesign your interior in seconds using AI
Transform rooms with AI-generated designs.
Transform Your Space with AI-Powered Japanese Interior Designs by Jife
AI-powered wireframes for non-designers.
Revolutionize Property Viewing with AI-Generated Floor Plans and Virtual Tours
Transform sketches into stunning architectural renders with AI.