[{"data":1,"prerenderedAt":-1},["ShallowReactive",2],{"skill:01M71H95PYGAWBS5C8KN0C4WGT":3,"skill-same:01M71H95PYGAWBS5C8KN0C4WGT":62},{"skillNo":4,"name":5,"nameZh":6,"description":7,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":6,"repositoryNo":12,"starsCount":13,"licenseSpdxId":14,"licenseFileUrl":6,"sourceHtmlUrl":15,"categories":16,"keywords":22,"publishedAt":37,"linkOnly":38,"markdownContent":39,"markdownBody":40,"frontMatter":41,"refName":42,"commitSha":43,"skillDirectory":44,"forksCount":13,"repositoryHtmlUrl":45,"repositoryHomepageUrl":6,"ownerHtmlUrl":46,"ownerBlogUrl":6,"ownerDescription":6,"updatedAt":47,"install":48,"resources":61},"01M71H95PYGAWBS5C8KN0C4WGT","react-animation",null,"React animation with react\u002Fmotion (Framer Motion) — declarative animations, gestures, layout animations, and performance best practices","lambda-solver\u002Flambdabench-pro","lambda-solver","https:\u002F\u002Favatars.githubusercontent.com\u002Fu\u002F279071046?v=4","01M71H55W2WK3PM6RRAY9JZWAJ","01M71H55WRT9XAFDAVACTCC2XP",0,"MIT","https:\u002F\u002Fgithub.com\u002Flambda-solver\u002Flambdabench-pro\u002Fblob\u002Fmain\u002Freference\u002Fghui\u002F.opencode\u002Fskills\u002Freact\u002Fanimation\u002FSKILL.md",[17],{"categoryNo":18,"categoryName":19,"taxonomyType":20,"taxonomyTypeDesc":21},"01JBSEEDCAT000000000000009","前端开发","SCENARIO","使用场景",[23,24,25,26,27,28,29,30,31,32,33,34,35,36],"Animation","react","react animation","performance","framer motion","opencode","layout","framer","gestures","animations","declarative","motion","best practices","with","2026-11-10 10:12:30",false,"---\nname: react-animation\ndescription: React animation with react\u002Fmotion (Framer Motion) — declarative animations, gestures, layout animations, and performance best practices\nlicense: MIT\ncompatibility: opencode\n---\n\n# React Animation with `react\u002Fmotion`\n\nDeclarative animation patterns for React using `react\u002Fmotion` (Framer Motion v12+).\nThese rules apply to all animated UI components.\n\n## Installation\n\n```bash\nbun add react\u002Fmotion\n```\n\n## Core Concepts\n\n### Motion Components\n\nUse `motion` prefix to animate any HTML or SVG element:\n\n```tsx\nimport { motion } from \"react\u002Fmotion\";\n\n\u002F\u002F Animate a div\n\u003Cmotion.div\n  initial={{ opacity: 0, y: 20 }}\n  animate={{ opacity: 1, y: 0 }}\n  transition={{ duration: 0.5 }}\n\u002F>\n\n\u002F\u002F Animate an SVG path\n\u003Cmotion.path\n  initial={{ pathLength: 0 }}\n  animate={{ pathLength: 1 }}\n  transition={{ duration: 2, ease: \"easeInOut\" }}\n\u002F>\n```\n\n### The `animate` Prop\n\nDrive animations with the `animate` prop. It accepts:\n\n- **Static values**: `animate={{ opacity: 1 }}`\n- **Dynamic values**: `animate={{ x: isOpen ? 0 : -100 }}`\n- **Variants**: `animate=\"visible\"` (with `variants` object)\n\n```tsx\n\u002F\u002F Static\n\u003Cmotion.div animate={{ scale: 1.2 }} \u002F>\n\n\u002F\u002F Dynamic based on state\n\u003Cmotion.div animate={{ x: isOpen ? 0 : -300 }} \u002F>\n\n\u002F\u002F Variants (preferred for complex orchestration)\nconst variants = {\n  hidden: { opacity: 0, x: -100 },\n  visible: { opacity: 1, x: 0 },\n};\n\n\u003Cmotion.div variants={variants} initial=\"hidden\" animate=\"visible\" \u002F>\n```\n\n### Transitions\n\nControl timing with the `transition` prop:\n\n```tsx\n\u002F\u002F Tween (default)\n\u003Cmotion.div\n  animate={{ x: 100 }}\n  transition={{ duration: 0.5, ease: \"easeOut\" }}\n\u002F>\n\n\u002F\u002F Spring (natural physics)\n\u003Cmotion.div\n  animate={{ x: 100 }}\n  transition={{ type: \"spring\", stiffness: 300, damping: 20 }}\n\u002F>\n\n\u002F\u002F Custom cubic bezier\n\u003Cmotion.div\n  animate={{ x: 100 }}\n  transition={{ duration: 0.6, ease: [0.4, 0, 0.2, 1] }}\n\u002F>\n```\n\n**Transition presets:**\n\n| Preset | Use Case |\n|--------|----------|\n| `type: \"spring\"` | Natural, physical movement (drag, scale, position) |\n| `type: \"tween\"` | Precise timing (opacity, color, choreographed sequences) |\n| `ease: \"easeInOut\"` | Smooth start\u002Fend (modals, page transitions) |\n| `ease: [0.4, 0, 0.2, 1]` | Material Design standard (buttons, cards) |\n| `ease: [0.22, 1, 0.36, 1]` | Exponential ease-out (hero animations) |\n\n## Gestures\n\n### Hover\n\n```tsx\n\u003Cmotion.div\n  whileHover={{ scale: 1.05 }}\n  whileTap={{ scale: 0.95 }}\n  transition={{ type: \"spring\", stiffness: 400, damping: 17 }}\n\u002F>\n```\n\n### Drag\n\n```tsx\n\u003Cmotion.div\n  drag\n  dragConstraints={{ left: -100, right: 100, top: -100, bottom: 100 }}\n  dragElastic={0.2}\n  whileDrag={{ scale: 1.1 }}\n\u002F>\n```\n\n### Scroll-Linked\n\n```tsx\nconst { scrollYProgress } = useScroll();\n\n\u003Cmotion.div\n  style={{ scaleX: scrollYProgress }}\n  className=\"fixed top-0 left-0 right-0 h-1 bg-blue-500 origin-left\"\n\u002F>\n```\n\n## Enter\u002FExit Animations with `AnimatePresence`\n\n```tsx\nimport { AnimatePresence, motion } from \"react\u002Fmotion\";\n\nfunction List({ items }) {\n  return (\n    \u003CAnimatePresence mode=\"popLayout\">\n      {items.map((item) => (\n        \u003Cmotion.div\n          key={item.id}\n          layout\n          initial={{ opacity: 0, scale: 0.8 }}\n          animate={{ opacity: 1, scale: 1 }}\n          exit={{ opacity: 0, scale: 0.8 }}\n          transition={{ type: \"spring\", stiffness: 300, damping: 25 }}\n        >\n          {item.content}\n        \u003C\u002Fmotion.div>\n      ))}\n    \u003C\u002FAnimatePresence>\n  );\n}\n```\n\n**`AnimatePresence` rules:**\n\n- Always provide a unique `key` for children\n- Use `mode=\"popLayout\"` for reordering animations\n- Use `mode=\"wait\"` when entering\u002Fexiting elements should not overlap\n\n## Layout Animations\n\nEnable automatic layout animations with the `layout` prop:\n\n```tsx\n\u002F\u002F Automatically animates position\u002Fsize changes\n\u003Cmotion.div layout className=\"bg-blue-500 rounded-lg\">\n  {content}\n\u003C\u002Fmotion.div>\n\n\u002F\u002F Layout + shared layout ID for morphing\n\u003Cmotion.div\n  layoutId=\"card\"\n  className=\"bg-blue-500 rounded-lg\"\n\u002F>\n```\n\n## Great Animation Examples\n\n### 1. Staggered List Entrance\n\n```tsx\nconst container = {\n  hidden: { opacity: 0 },\n  visible: {\n    opacity: 1,\n    transition: { staggerChildren: 0.1, delayChildren: 0.2 },\n  },\n};\n\nconst item = {\n  hidden: { opacity: 0, y: 20 },\n  visible: { opacity: 1, y: 0, transition: { type: \"spring\", stiffness: 300 } },\n};\n\n\u003Cmotion.ul variants={container} initial=\"hidden\" animate=\"visible\">\n  {items.map((item) => (\n    \u003Cmotion.li key={item.id} variants={item}>\n      {item.name}\n    \u003C\u002Fmotion.li>\n  ))}\n\u003C\u002Fmotion.ul>\n```\n\n### 2. Smooth Page Transition\n\n```tsx\nfunction PageTransition({ children }) {\n  return (\n    \u003Cmotion.div\n      initial={{ opacity: 0, y: 20 }}\n      animate={{ opacity: 1, y: 0 }}\n      exit={{ opacity: 0, y: -20 }}\n      transition={{ duration: 0.4, ease: [0.4, 0, 0.2, 1] }}\n    >\n      {children}\n    \u003C\u002Fmotion.div>\n  );\n}\n```\n\n### 3. Loading Skeleton Pulse\n\n```tsx\n\u003Cmotion.div\n  animate={{ opacity: [0.5, 1, 0.5] }}\n  transition={{ duration: 1.5, repeat: Infinity, ease: \"easeInOut\" }}\n  className=\"h-4 bg-gray-200 rounded\"\n\u002F>\n```\n\n### 4. Progress Bar with Spring\n\n```tsx\n\u003Cmotion.div\n  className=\"h-1 bg-blue-500\"\n  initial={{ width: \"0%\" }}\n  animate={{ width: `${progress}%` }}\n  transition={{ type: \"spring\", stiffness: 50, damping: 15 }}\n\u002F>\n```\n\n### 5. Card Hover Lift\n\n```tsx\n\u003Cmotion.div\n  whileHover={{ y: -4, boxShadow: \"0 10px 40px rgba(0,0,0,0.1)\" }}\n  transition={{ type: \"spring\", stiffness: 300, damping: 20 }}\n  className=\"bg-white rounded-xl p-6\"\n>\n  Content\n\u003C\u002Fmotion.div>\n```\n\n### 6. Modal Overlay\n\n```tsx\n\u003CAnimatePresence>\n  {isOpen && (\n    \u003C>\n      \u003Cmotion.div\n        initial={{ opacity: 0 }}\n        animate={{ opacity: 1 }}\n        exit={{ opacity: 0 }}\n        className=\"fixed inset-0 bg-black\u002F50\"\n        onClick={onClose}\n      \u002F>\n      \u003Cmotion.div\n        initial={{ opacity: 0, scale: 0.95, y: 20 }}\n        animate={{ opacity: 1, scale: 1, y: 0 }}\n        exit={{ opacity: 0, scale: 0.95, y: 20 }}\n        transition={{ type: \"spring\", stiffness: 300, damping: 25 }}\n        className=\"fixed inset-0 m-auto w-full max-w-md h-fit bg-white rounded-2xl p-6\"\n      >\n        {content}\n      \u003C\u002Fmotion.div>\n    \u003C\u002F>\n  )}\n\u003C\u002FAnimatePresence>\n```\n\n### 7. Scroll-Triggered Fade In\n\n```tsx\nfunction FadeIn({ children }) {\n  const ref = useRef(null);\n  const isInView = useInView(ref, { once: true, margin: \"-100px\" });\n\n  return (\n    \u003Cmotion.div\n      ref={ref}\n      initial={{ opacity: 0, y: 30 }}\n      animate={isInView ? { opacity: 1, y: 0 } : { opacity: 0, y: 30 }}\n      transition={{ duration: 0.6, ease: [0.4, 0, 0.2, 1] }}\n    >\n      {children}\n    \u003C\u002Fmotion.div>\n  );\n}\n```\n\n### 8. Micro-interaction Button\n\n```tsx\n\u003Cmotion.button\n  whileHover={{ scale: 1.02 }}\n  whileTap={{ scale: 0.98 }}\n  whileFocus={{ boxShadow: \"0 0 0 3px rgba(59, 130, 246, 0.5)\" }}\n  transition={{ type: \"spring\", stiffness: 400, damping: 17 }}\n  className=\"px-6 py-3 bg-blue-500 text-white rounded-lg\"\n>\n  Click me\n\u003C\u002Fmotion.button>\n```\n\n### 9. Complex Multi-State Feature Preview (Browser Mockup)\n\nA real-world example showing how to orchestrate multiple overlapping animations based on application state. This pattern is ideal for landing pages and documentation that demonstrate product features through animated UI mockups.\n\n**Key techniques demonstrated:**\n- **Conditional animations**: Different `animate` states based on boolean flags (`focused`, `fixing`, `reloading`)\n- **Spring configuration from props**: Externalizing physics parameters for design system consistency\n- **Overlapping motion layers**: Multiple `motion.div` elements animating independently within the same component\n- **AnimatePresence for conditional content**: Enter\u002Fexit animations for elements that appear based on state\n- **Progress bar sequencing**: Chained width animations simulating a loading process\n- **Subtle overlay effects**: Low-opacity overlays that respond to interaction state\n\n```tsx\nimport { motion, AnimatePresence } from \"react\u002Fmotion\";\nimport { useState, useEffect } from \"react\";\n\ninterface AnimationConfig {\n  browserSpringStiffness: number;\n  browserSpringDamping: number;\n  browserSpringMass: number;\n}\n\ninterface BrowserPreviewProps {\n  slid: boolean;      \u002F\u002F Has the browser slid into view?\n  focused: boolean;   \u002F\u002F Is the browser being interacted with?\n  fixing: boolean;    \u002F\u002F Is a fix being applied?\n  fixDiff: boolean;   \u002F\u002F Show the fix diff panel?\n  reloading: boolean; \u002F\u002F Is the page reloading?\n  reloadDone: boolean; \u002F\u002F Has reload completed?\n  config: AnimationConfig;\n}\n\nfunction BrowserPreview({\n  slid,\n  focused,\n  fixing,\n  fixDiff,\n  reloading,\n  reloadDone,\n  config,\n}: BrowserPreviewProps) {\n  const loading = slid;\n  const [loaded, setLoaded] = useState(false);\n\n  \u002F\u002F Simulate loading completion after slide-in\n  useEffect(() => {\n    if (!slid) return;\n    const timer = setTimeout(() => setLoaded(true), 600);\n    return () => clearTimeout(timer);\n  }, [slid]);\n\n  return (\n    \u003Cmotion.div\n      className=\"absolute top-0 left-0\"\n      suppressHydrationWarning\n      initial={false}\n      \u002F\u002F Conditional animation: focused state gets scale + zIndex boost\n      animate={\n        focused\n          ? { x: -90, y: -8, scale: 1.04, zIndex: 20 }\n          : { x: -90, y: -8, scale: 1, zIndex: 0 }\n      }\n      \u002F\u002F Spring physics from config for consistent feel across the app\n      transition={{\n        type: \"spring\",\n        stiffness: config.browserSpringStiffness,\n        damping: config.browserSpringDamping,\n        mass: config.browserSpringMass \u002F 1000,\n      }}\n    >\n      \u003Cdiv className=\"relative flex flex-col w-68.5 h-46 rounded-2xl bg-white overflow-hidden\">\n        {\u002F* Browser chrome: traffic lights + address bar *\u002F}\n        \u003Cdiv className=\"flex items-center\">\n          \u003Cdiv className=\"flex gap-1.5\">\n            \u003Cdiv className=\"rounded-full bg-[#FF726A] size-2.5\" \u002F>\n            \u003Cdiv className=\"rounded-full bg-[#FEBC2E] size-2.5\" \u002F>\n            \u003Cdiv className=\"rounded-full bg-[#EAEAEA] size-2.5\" \u002F>\n          \u003C\u002Fdiv>\n\n          {\u002F* Address bar with loading progress *\u002F}\n          \u003Cdiv className=\"relative w-36.25 h-6.5 rounded-full bg-white overflow-hidden flex items-center justify-center\">\n            \u003Cspan className=\"text-[12px] text-[#888888]\">localhost\u003C\u002Fspan>\n\n            {\u002F* Loading progress bar - sequenced animations *\u002F}\n            {loading && !loaded && (\n              \u003Cmotion.div\n                className=\"absolute bottom-0 left-0 h-[2.5px] bg-[#007AFF]\"\n                initial={{ width: \"0%\" }}\n                animate={{ width: \"85%\" }}\n                transition={{ duration: 0.6, ease: [0.4, 0, 0.2, 1] }}\n              \u002F>\n            )}\n            {loaded && !reloading && (\n              \u003Cmotion.div\n                className=\"absolute bottom-0 left-0 h-[2.5px] bg-[#007AFF]\"\n                initial={{ width: \"85%\" }}\n                animate={{ width: \"100%\", opacity: 0 }}\n                transition={{ duration: 0.2, ease: \"easeOut\" }}\n              \u002F>\n            )}\n            {reloading && !reloadDone && (\n              \u003Cmotion.div\n                className=\"absolute bottom-0 left-0 h-[2.5px] bg-[#007AFF]\"\n                initial={{ width: \"0%\" }}\n                animate={{ width: \"90%\" }}\n                transition={{ duration: 0.7, ease: [0.4, 0, 0.2, 1] }}\n              \u002F>\n            )}\n            {reloadDone && (\n              \u003Cmotion.div\n                className=\"absolute bottom-0 left-0 h-[2.5px] bg-[#007AFF]\"\n                initial={{ width: \"90%\" }}\n                animate={{ width: \"100%\", opacity: 0 }}\n                transition={{ duration: 0.15, ease: \"easeOut\" }}\n              \u002F>\n            )}\n          \u003C\u002Fdiv>\n        \u003C\u002Fdiv>\n\n        {\u002F* Page content with AnimatePresence for enter\u002Fexit *\u002F}\n        \u003CAnimatePresence>\n          {loaded && (\n            \u003Cmotion.div\n              initial={{ opacity: 0 }}\n              animate={{\n                opacity:\n                  (reloading && !reloadDone) || ((focused || fixing) && !reloading)\n                    ? 0\n                    : 1,\n              }}\n              transition={{\n                duration: reloading ? 0.15 : 0.4,\n                ease: \"easeOut\",\n              }}\n            >\n              {\u002F* Login form mockup *\u002F}\n              \u003Cdiv className=\"mt-4.5 text-[#474747] font-medium text-base\">\n                login\n              \u003C\u002Fdiv>\n              \u003Cdiv className=\"mt-2 flex flex-col gap-3\">\n                \u003Cdiv className=\"flex w-52.75 h-7 items-center rounded-full bg-white px-3\">\n                  \u003Cspan className=\"text-[12.5px] text-[#474747]\">\n                    foo@bar.xyz\n                  \u003C\u002Fspan>\n                \u003C\u002Fdiv>\n                \u003Cdiv className=\"w-18 h-6 rounded-full bg-blue-500\" \u002F>\n              \u003C\u002Fdiv>\n            \u003C\u002Fmotion.div>\n          )}\n        \u003C\u002FAnimatePresence>\n\n        {\u002F* Subtle overlay on focus\u002Ffix *\u002F}\n        \u003Cmotion.div\n          className=\"absolute inset-0 bg-black pointer-events-none rounded-2xl\"\n          initial={{ opacity: 0 }}\n          animate={{\n            opacity: (focused || fixing) && !reloading ? 0.015 : 0,\n          }}\n          transition={{ duration: reloading ? 0.15 : 0.3 }}\n        \u002F>\n\n        {\u002F* Bottom panel that slides up on focus\u002Ffix *\u002F}\n        \u003Cmotion.div\n          className=\"absolute bottom-0 left-0 right-0 z-20\"\n          initial={{ y: \"100%\", opacity: 1 }}\n          animate={{\n            y: (focused || fixing) && !reloading ? \"0%\" : \"100%\",\n            opacity: reloading ? 0 : 1,\n          }}\n          transition={{\n            y: { type: \"spring\", stiffness: 400, damping: 30 },\n            opacity: { duration: 0.15, ease: \"easeOut\" },\n          }}\n        >\n          \u003CNetworkPanel fixed={fixDiff} \u002F>\n        \u003C\u002Fmotion.div>\n      \u003C\u002Fdiv>\n    \u003C\u002Fmotion.div>\n  );\n}\n```\n\n**Why this works:**\n\n1. **State-driven animation**: Every `animate` prop is a pure function of props (`focused`, `fixing`, `reloading`, etc.). No imperative animation calls.\n2. **Layered motion**: The component has 4 independent motion layers (container, content, overlay, bottom panel) that animate based on the same state but with different transitions.\n3. **Physics consistency**: Spring parameters come from a shared `config` object, ensuring all browser mockups in the app feel the same.\n4. **Sequenced loading**: The progress bar uses 4 distinct motion states (loading → loaded → reloading → reloadDone) with precise timing to tell a story.\n5. **Performance**: All animations use `transform` and `opacity` only. The overlay uses a very low opacity (0.015) to avoid visual heaviness while still providing focus indication.\n\n## Anti-Patterns\n\n### ❌ Animating Expensive Properties\n\n```tsx\n\u002F\u002F BAD — triggers layout recalculation every frame\n\u003Cmotion.div animate={{ width: 100, height: 100, left: 200, top: 200 }} \u002F>\n\n\u002F\u002F GOOD — use transform-only properties\n\u003Cmotion.div animate={{ scale: 1.5, x: 200, y: 200 }} \u002F>\n```\n\n**Always prefer:** `transform`, `opacity` over `width`, `height`, `top`, `left`, `margin`\n\n### ❌ Missing `key` in `AnimatePresence`\n\n```tsx\n\u002F\u002F BAD — AnimatePresence can't track elements\n\u003CAnimatePresence>\n  {items.map((item) => (\n    \u003Cmotion.div exit={{ opacity: 0 }}>{item}\u003C\u002Fmotion.div> \u002F\u002F ❌ no key\n  ))}\n\u003C\u002FAnimatePresence>\n\n\u002F\u002F GOOD\n\u003CAnimatePresence>\n  {items.map((item) => (\n    \u003Cmotion.div key={item.id} exit={{ opacity: 0 }}>\n      {item}\n    \u003C\u002Fmotion.div>\n  ))}\n\u003C\u002FAnimatePresence>\n```\n\n### ❌ Inline Object in `animate` Without Memoization\n\n```tsx\n\u002F\u002F BAD — new object every render, triggers re-animation\n\u003Cmotion.div animate={{ opacity: isVisible ? 1 : 0 }} \u002F>\n\n\u002F\u002F GOOD — use variants or memoize\nconst variants = { visible: { opacity: 1 }, hidden: { opacity: 0 } };\n\u003Cmotion.div variants={variants} animate={isVisible ? \"visible\" : \"hidden\"} \u002F>\n```\n\n### ❌ Overusing Spring for Simple Transitions\n\n```tsx\n\u002F\u002F BAD — spring for opacity feels sluggish\n\u003Cmotion.div\n  animate={{ opacity: 1 }}\n  transition={{ type: \"spring\", stiffness: 100 }}\n\u002F>\n\n\u002F\u002F GOOD — tween for opacity, spring for position\n\u003Cmotion.div\n  animate={{ opacity: 1, y: 0 }}\n  transition={{\n    opacity: { duration: 0.3 },\n    y: { type: \"spring\", stiffness: 300 },\n  }}\n\u002F>\n```\n\n### ❌ Animating During Scroll Without `useScroll`\n\n```tsx\n\u002F\u002F BAD — manual scroll listener + setState = jank\nuseEffect(() => {\n  const handleScroll = () => setScrollY(window.scrollY);\n  window.addEventListener(\"scroll\", handleScroll);\n}, []);\n\n\u002F\u002F GOOD — useScroll hook (optimized, RAF-driven)\nconst { scrollY } = useScroll();\nconst scale = useTransform(scrollY, [0, 500], [1, 0.8]);\n\u003Cmotion.div style={{ scale }} \u002F>\n```\n\n### ❌ Nested `motion` Components Without `layout`\n\n```tsx\n\u002F\u002F BAD — parent moves but children jump\n\u003Cmotion.div animate={{ x: 100 }}>\n  \u003Cdiv>Child jumps\u003C\u002Fdiv>\n\u003C\u002Fmotion.div>\n\n\u002F\u002F GOOD — use layout for smooth child transitions\n\u003Cmotion.div animate={{ x: 100 }}>\n  \u003Cmotion.div layout>Child animates smoothly\u003C\u002Fmotion.div>\n\u003C\u002Fmotion.div>\n```\n\n### ❌ Heavy Computation in `useTransform`\n\n```tsx\n\u002F\u002F BAD — runs on every frame\nconst value = useTransform(scrollY, (v) => expensiveCalculation(v));\n\n\u002F\u002F GOOD — pre-compute or use useMotionValueEvent\nconst value = useTransform(scrollY, [0, 1000], [0, 1]);\n```\n\n## Best Practices\n\n### 1. Define Variants Outside Components\n\n```tsx\n\u002F\u002F ✅ Defined once, reused, no re-renders\nconst cardVariants = {\n  rest: { scale: 1 },\n  hover: { scale: 1.02 },\n};\n\nfunction Card() {\n  return (\n    \u003Cmotion.div variants={cardVariants} initial=\"rest\" whileHover=\"hover\">\n      Content\n    \u003C\u002Fmotion.div>\n  );\n}\n```\n\n### 2. Use `will-change` Sparingly\n\n```tsx\n\u002F\u002F ✅ Only on actively animating elements\n\u003Cmotion.div\n  style={{ willChange: \"transform\" }}\n  animate={{ x: 100 }}\n\u002F>\n```\n\n### 3. Respect `prefers-reduced-motion`\n\n```tsx\nconst prefersReducedMotion = useReducedMotion();\n\n\u003Cmotion.div\n  animate={prefersReducedMotion ? {} : { x: 100 }}\n\u002F>\n```\n\n### 4. Lazy Load Heavy Animations\n\n```tsx\nimport { lazy, Suspense } from \"react\";\n\nconst HeavyAnimation = lazy(() => import(\".\u002FHeavyAnimation\"));\n\n\u003CSuspense fallback={\u003Cdiv>Loading...\u003C\u002Fdiv>}>\n  \u003CHeavyAnimation \u002F>\n\u003C\u002FSuspense>\n```\n\n### 5. Use `layoutId` for Shared Element Transitions\n\n```tsx\n\u002F\u002F Card on page A\n\u003Cmotion.div layoutId=\"card\" className=\"w-20 h-20 bg-blue-500\" \u002F>\n\n\u002F\u002F Expanded card on page B\n\u003Cmotion.div layoutId=\"card\" className=\"w-full h-64 bg-blue-500\" \u002F>\n```\n\n## Performance Checklist\n\n- [ ] Use `transform` and `opacity` only when possible\n- [ ] Add `will-change: transform` to actively animating elements\n- [ ] Use `useReducedMotion()` for accessibility\n- [ ] Avoid animating `box-shadow` and `blur` simultaneously\n- [ ] Use `layout` prop only when necessary (has overhead)\n- [ ] Memoize variant objects defined in components\n- [ ] Use `AnimatePresence` `mode=\"popLayout\"` for lists\n- [ ] Prefer `useScroll` over manual scroll listeners\n\n## Summary\n\n| Pattern | Do | Don't |\n|---------|-----|-------|\n| Properties | `transform`, `opacity` | `width`, `height`, `top`, `margin` |\n| Transitions | Spring for movement, tween for fades | Spring for opacity changes |\n| Lists | `AnimatePresence` + `layout` + stable `key` | Missing keys, no exit animations |\n| Scroll | `useScroll`, `useInView` | Manual `scroll` listeners + `setState` |\n| Gestures | `whileHover`, `whileTap`, `whileDrag` | Manual mouse event handlers |\n| Shared elements | `layoutId` | Manual position calculation |\n| Accessibility | `useReducedMotion()` | Ignore motion preferences |\n","# React Animation with `react\u002Fmotion`\n\nDeclarative animation patterns for React using `react\u002Fmotion` (Framer Motion v12+).\nThese rules apply to all animated UI components.\n\n## Installation\n\n```bash\nbun add react\u002Fmotion\n```\n\n## Core Concepts\n\n### Motion Components\n\nUse `motion` prefix to animate any HTML or SVG element:\n\n```tsx\nimport { motion } from \"react\u002Fmotion\";\n\n\u002F\u002F Animate a div\n\u003Cmotion.div\n  initial={{ opacity: 0, y: 20 }}\n  animate={{ opacity: 1, y: 0 }}\n  transition={{ duration: 0.5 }}\n\u002F>\n\n\u002F\u002F Animate an SVG path\n\u003Cmotion.path\n  initial={{ pathLength: 0 }}\n  animate={{ pathLength: 1 }}\n  transition={{ duration: 2, ease: \"easeInOut\" }}\n\u002F>\n```\n\n### The `animate` Prop\n\nDrive animations with the `animate` prop. It accepts:\n\n- **Static values**: `animate={{ opacity: 1 }}`\n- **Dynamic values**: `animate={{ x: isOpen ? 0 : -100 }}`\n- **Variants**: `animate=\"visible\"` (with `variants` object)\n\n```tsx\n\u002F\u002F Static\n\u003Cmotion.div animate={{ scale: 1.2 }} \u002F>\n\n\u002F\u002F Dynamic based on state\n\u003Cmotion.div animate={{ x: isOpen ? 0 : -300 }} \u002F>\n\n\u002F\u002F Variants (preferred for complex orchestration)\nconst variants = {\n  hidden: { opacity: 0, x: -100 },\n  visible: { opacity: 1, x: 0 },\n};\n\n\u003Cmotion.div variants={variants} initial=\"hidden\" animate=\"visible\" \u002F>\n```\n\n### Transitions\n\nControl timing with the `transition` prop:\n\n```tsx\n\u002F\u002F Tween (default)\n\u003Cmotion.div\n  animate={{ x: 100 }}\n  transition={{ duration: 0.5, ease: \"easeOut\" }}\n\u002F>\n\n\u002F\u002F Spring (natural physics)\n\u003Cmotion.div\n  animate={{ x: 100 }}\n  transition={{ type: \"spring\", stiffness: 300, damping: 20 }}\n\u002F>\n\n\u002F\u002F Custom cubic bezier\n\u003Cmotion.div\n  animate={{ x: 100 }}\n  transition={{ duration: 0.6, ease: [0.4, 0, 0.2, 1] }}\n\u002F>\n```\n\n**Transition presets:**\n\n| Preset | Use Case |\n|--------|----------|\n| `type: \"spring\"` | Natural, physical movement (drag, scale, position) |\n| `type: \"tween\"` | Precise timing (opacity, color, choreographed sequences) |\n| `ease: \"easeInOut\"` | Smooth start\u002Fend (modals, page transitions) |\n| `ease: [0.4, 0, 0.2, 1]` | Material Design standard (buttons, cards) |\n| `ease: [0.22, 1, 0.36, 1]` | Exponential ease-out (hero animations) |\n\n## Gestures\n\n### Hover\n\n```tsx\n\u003Cmotion.div\n  whileHover={{ scale: 1.05 }}\n  whileTap={{ scale: 0.95 }}\n  transition={{ type: \"spring\", stiffness: 400, damping: 17 }}\n\u002F>\n```\n\n### Drag\n\n```tsx\n\u003Cmotion.div\n  drag\n  dragConstraints={{ left: -100, right: 100, top: -100, bottom: 100 }}\n  dragElastic={0.2}\n  whileDrag={{ scale: 1.1 }}\n\u002F>\n```\n\n### Scroll-Linked\n\n```tsx\nconst { scrollYProgress } = useScroll();\n\n\u003Cmotion.div\n  style={{ scaleX: scrollYProgress }}\n  className=\"fixed top-0 left-0 right-0 h-1 bg-blue-500 origin-left\"\n\u002F>\n```\n\n## Enter\u002FExit Animations with `AnimatePresence`\n\n```tsx\nimport { AnimatePresence, motion } from \"react\u002Fmotion\";\n\nfunction List({ items }) {\n  return (\n    \u003CAnimatePresence mode=\"popLayout\">\n      {items.map((item) => (\n        \u003Cmotion.div\n          key={item.id}\n          layout\n          initial={{ opacity: 0, scale: 0.8 }}\n          animate={{ opacity: 1, scale: 1 }}\n          exit={{ opacity: 0, scale: 0.8 }}\n          transition={{ type: \"spring\", stiffness: 300, damping: 25 }}\n        >\n          {item.content}\n        \u003C\u002Fmotion.div>\n      ))}\n    \u003C\u002FAnimatePresence>\n  );\n}\n```\n\n**`AnimatePresence` rules:**\n\n- Always provide a unique `key` for children\n- Use `mode=\"popLayout\"` for reordering animations\n- Use `mode=\"wait\"` when entering\u002Fexiting elements should not overlap\n\n## Layout Animations\n\nEnable automatic layout animations with the `layout` prop:\n\n```tsx\n\u002F\u002F Automatically animates position\u002Fsize changes\n\u003Cmotion.div layout className=\"bg-blue-500 rounded-lg\">\n  {content}\n\u003C\u002Fmotion.div>\n\n\u002F\u002F Layout + shared layout ID for morphing\n\u003Cmotion.div\n  layoutId=\"card\"\n  className=\"bg-blue-500 rounded-lg\"\n\u002F>\n```\n\n## Great Animation Examples\n\n### 1. Staggered List Entrance\n\n```tsx\nconst container = {\n  hidden: { opacity: 0 },\n  visible: {\n    opacity: 1,\n    transition: { staggerChildren: 0.1, delayChildren: 0.2 },\n  },\n};\n\nconst item = {\n  hidden: { opacity: 0, y: 20 },\n  visible: { opacity: 1, y: 0, transition: { type: \"spring\", stiffness: 300 } },\n};\n\n\u003Cmotion.ul variants={container} initial=\"hidden\" animate=\"visible\">\n  {items.map((item) => (\n    \u003Cmotion.li key={item.id} variants={item}>\n      {item.name}\n    \u003C\u002Fmotion.li>\n  ))}\n\u003C\u002Fmotion.ul>\n```\n\n### 2. Smooth Page Transition\n\n```tsx\nfunction PageTransition({ children }) {\n  return (\n    \u003Cmotion.div\n      initial={{ opacity: 0, y: 20 }}\n      animate={{ opacity: 1, y: 0 }}\n      exit={{ opacity: 0, y: -20 }}\n      transition={{ duration: 0.4, ease: [0.4, 0, 0.2, 1] }}\n    >\n      {children}\n    \u003C\u002Fmotion.div>\n  );\n}\n```\n\n### 3. Loading Skeleton Pulse\n\n```tsx\n\u003Cmotion.div\n  animate={{ opacity: [0.5, 1, 0.5] }}\n  transition={{ duration: 1.5, repeat: Infinity, ease: \"easeInOut\" }}\n  className=\"h-4 bg-gray-200 rounded\"\n\u002F>\n```\n\n### 4. Progress Bar with Spring\n\n```tsx\n\u003Cmotion.div\n  className=\"h-1 bg-blue-500\"\n  initial={{ width: \"0%\" }}\n  animate={{ width: `${progress}%` }}\n  transition={{ type: \"spring\", stiffness: 50, damping: 15 }}\n\u002F>\n```\n\n### 5. Card Hover Lift\n\n```tsx\n\u003Cmotion.div\n  whileHover={{ y: -4, boxShadow: \"0 10px 40px rgba(0,0,0,0.1)\" }}\n  transition={{ type: \"spring\", stiffness: 300, damping: 20 }}\n  className=\"bg-white rounded-xl p-6\"\n>\n  Content\n\u003C\u002Fmotion.div>\n```\n\n### 6. Modal Overlay\n\n```tsx\n\u003CAnimatePresence>\n  {isOpen && (\n    \u003C>\n      \u003Cmotion.div\n        initial={{ opacity: 0 }}\n        animate={{ opacity: 1 }}\n        exit={{ opacity: 0 }}\n        className=\"fixed inset-0 bg-black\u002F50\"\n        onClick={onClose}\n      \u002F>\n      \u003Cmotion.div\n        initial={{ opacity: 0, scale: 0.95, y: 20 }}\n        animate={{ opacity: 1, scale: 1, y: 0 }}\n        exit={{ opacity: 0, scale: 0.95, y: 20 }}\n        transition={{ type: \"spring\", stiffness: 300, damping: 25 }}\n        className=\"fixed inset-0 m-auto w-full max-w-md h-fit bg-white rounded-2xl p-6\"\n      >\n        {content}\n      \u003C\u002Fmotion.div>\n    \u003C\u002F>\n  )}\n\u003C\u002FAnimatePresence>\n```\n\n### 7. Scroll-Triggered Fade In\n\n```tsx\nfunction FadeIn({ children }) {\n  const ref = useRef(null);\n  const isInView = useInView(ref, { once: true, margin: \"-100px\" });\n\n  return (\n    \u003Cmotion.div\n      ref={ref}\n      initial={{ opacity: 0, y: 30 }}\n      animate={isInView ? { opacity: 1, y: 0 } : { opacity: 0, y: 30 }}\n      transition={{ duration: 0.6, ease: [0.4, 0, 0.2, 1] }}\n    >\n      {children}\n    \u003C\u002Fmotion.div>\n  );\n}\n```\n\n### 8. Micro-interaction Button\n\n```tsx\n\u003Cmotion.button\n  whileHover={{ scale: 1.02 }}\n  whileTap={{ scale: 0.98 }}\n  whileFocus={{ boxShadow: \"0 0 0 3px rgba(59, 130, 246, 0.5)\" }}\n  transition={{ type: \"spring\", stiffness: 400, damping: 17 }}\n  className=\"px-6 py-3 bg-blue-500 text-white rounded-lg\"\n>\n  Click me\n\u003C\u002Fmotion.button>\n```\n\n### 9. Complex Multi-State Feature Preview (Browser Mockup)\n\nA real-world example showing how to orchestrate multiple overlapping animations based on application state. This pattern is ideal for landing pages and documentation that demonstrate product features through animated UI mockups.\n\n**Key techniques demonstrated:**\n- **Conditional animations**: Different `animate` states based on boolean flags (`focused`, `fixing`, `reloading`)\n- **Spring configuration from props**: Externalizing physics parameters for design system consistency\n- **Overlapping motion layers**: Multiple `motion.div` elements animating independently within the same component\n- **AnimatePresence for conditional content**: Enter\u002Fexit animations for elements that appear based on state\n- **Progress bar sequencing**: Chained width animations simulating a loading process\n- **Subtle overlay effects**: Low-opacity overlays that respond to interaction state\n\n```tsx\nimport { motion, AnimatePresence } from \"react\u002Fmotion\";\nimport { useState, useEffect } from \"react\";\n\ninterface AnimationConfig {\n  browserSpringStiffness: number;\n  browserSpringDamping: number;\n  browserSpringMass: number;\n}\n\ninterface BrowserPreviewProps {\n  slid: boolean;      \u002F\u002F Has the browser slid into view?\n  focused: boolean;   \u002F\u002F Is the browser being interacted with?\n  fixing: boolean;    \u002F\u002F Is a fix being applied?\n  fixDiff: boolean;   \u002F\u002F Show the fix diff panel?\n  reloading: boolean; \u002F\u002F Is the page reloading?\n  reloadDone: boolean; \u002F\u002F Has reload completed?\n  config: AnimationConfig;\n}\n\nfunction BrowserPreview({\n  slid,\n  focused,\n  fixing,\n  fixDiff,\n  reloading,\n  reloadDone,\n  config,\n}: BrowserPreviewProps) {\n  const loading = slid;\n  const [loaded, setLoaded] = useState(false);\n\n  \u002F\u002F Simulate loading completion after slide-in\n  useEffect(() => {\n    if (!slid) return;\n    const timer = setTimeout(() => setLoaded(true), 600);\n    return () => clearTimeout(timer);\n  }, [slid]);\n\n  return (\n    \u003Cmotion.div\n      className=\"absolute top-0 left-0\"\n      suppressHydrationWarning\n      initial={false}\n      \u002F\u002F Conditional animation: focused state gets scale + zIndex boost\n      animate={\n        focused\n          ? { x: -90, y: -8, scale: 1.04, zIndex: 20 }\n          : { x: -90, y: -8, scale: 1, zIndex: 0 }\n      }\n      \u002F\u002F Spring physics from config for consistent feel across the app\n      transition={{\n        type: \"spring\",\n        stiffness: config.browserSpringStiffness,\n        damping: config.browserSpringDamping,\n        mass: config.browserSpringMass \u002F 1000,\n      }}\n    >\n      \u003Cdiv className=\"relative flex flex-col w-68.5 h-46 rounded-2xl bg-white overflow-hidden\">\n        {\u002F* Browser chrome: traffic lights + address bar *\u002F}\n        \u003Cdiv className=\"flex items-center\">\n          \u003Cdiv className=\"flex gap-1.5\">\n            \u003Cdiv className=\"rounded-full bg-[#FF726A] size-2.5\" \u002F>\n            \u003Cdiv className=\"rounded-full bg-[#FEBC2E] size-2.5\" \u002F>\n            \u003Cdiv className=\"rounded-full bg-[#EAEAEA] size-2.5\" \u002F>\n          \u003C\u002Fdiv>\n\n          {\u002F* Address bar with loading progress *\u002F}\n          \u003Cdiv className=\"relative w-36.25 h-6.5 rounded-full bg-white overflow-hidden flex items-center justify-center\">\n            \u003Cspan className=\"text-[12px] text-[#888888]\">localhost\u003C\u002Fspan>\n\n            {\u002F* Loading progress bar - sequenced animations *\u002F}\n            {loading && !loaded && (\n              \u003Cmotion.div\n                className=\"absolute bottom-0 left-0 h-[2.5px] bg-[#007AFF]\"\n                initial={{ width: \"0%\" }}\n                animate={{ width: \"85%\" }}\n                transition={{ duration: 0.6, ease: [0.4, 0, 0.2, 1] }}\n              \u002F>\n            )}\n            {loaded && !reloading && (\n              \u003Cmotion.div\n                className=\"absolute bottom-0 left-0 h-[2.5px] bg-[#007AFF]\"\n                initial={{ width: \"85%\" }}\n                animate={{ width: \"100%\", opacity: 0 }}\n                transition={{ duration: 0.2, ease: \"easeOut\" }}\n              \u002F>\n            )}\n            {reloading && !reloadDone && (\n              \u003Cmotion.div\n                className=\"absolute bottom-0 left-0 h-[2.5px] bg-[#007AFF]\"\n                initial={{ width: \"0%\" }}\n                animate={{ width: \"90%\" }}\n                transition={{ duration: 0.7, ease: [0.4, 0, 0.2, 1] }}\n              \u002F>\n            )}\n            {reloadDone && (\n              \u003Cmotion.div\n                className=\"absolute bottom-0 left-0 h-[2.5px] bg-[#007AFF]\"\n                initial={{ width: \"90%\" }}\n                animate={{ width: \"100%\", opacity: 0 }}\n                transition={{ duration: 0.15, ease: \"easeOut\" }}\n              \u002F>\n            )}\n          \u003C\u002Fdiv>\n        \u003C\u002Fdiv>\n\n        {\u002F* Page content with AnimatePresence for enter\u002Fexit *\u002F}\n        \u003CAnimatePresence>\n          {loaded && (\n            \u003Cmotion.div\n              initial={{ opacity: 0 }}\n              animate={{\n                opacity:\n                  (reloading && !reloadDone) || ((focused || fixing) && !reloading)\n                    ? 0\n                    : 1,\n              }}\n              transition={{\n                duration: reloading ? 0.15 : 0.4,\n                ease: \"easeOut\",\n              }}\n            >\n              {\u002F* Login form mockup *\u002F}\n              \u003Cdiv className=\"mt-4.5 text-[#474747] font-medium text-base\">\n                login\n              \u003C\u002Fdiv>\n              \u003Cdiv className=\"mt-2 flex flex-col gap-3\">\n                \u003Cdiv className=\"flex w-52.75 h-7 items-center rounded-full bg-white px-3\">\n                  \u003Cspan className=\"text-[12.5px] text-[#474747]\">\n                    foo@bar.xyz\n                  \u003C\u002Fspan>\n                \u003C\u002Fdiv>\n                \u003Cdiv className=\"w-18 h-6 rounded-full bg-blue-500\" \u002F>\n              \u003C\u002Fdiv>\n            \u003C\u002Fmotion.div>\n          )}\n        \u003C\u002FAnimatePresence>\n\n        {\u002F* Subtle overlay on focus\u002Ffix *\u002F}\n        \u003Cmotion.div\n          className=\"absolute inset-0 bg-black pointer-events-none rounded-2xl\"\n          initial={{ opacity: 0 }}\n          animate={{\n            opacity: (focused || fixing) && !reloading ? 0.015 : 0,\n          }}\n          transition={{ duration: reloading ? 0.15 : 0.3 }}\n        \u002F>\n\n        {\u002F* Bottom panel that slides up on focus\u002Ffix *\u002F}\n        \u003Cmotion.div\n          className=\"absolute bottom-0 left-0 right-0 z-20\"\n          initial={{ y: \"100%\", opacity: 1 }}\n          animate={{\n            y: (focused || fixing) && !reloading ? \"0%\" : \"100%\",\n            opacity: reloading ? 0 : 1,\n          }}\n          transition={{\n            y: { type: \"spring\", stiffness: 400, damping: 30 },\n            opacity: { duration: 0.15, ease: \"easeOut\" },\n          }}\n        >\n          \u003CNetworkPanel fixed={fixDiff} \u002F>\n        \u003C\u002Fmotion.div>\n      \u003C\u002Fdiv>\n    \u003C\u002Fmotion.div>\n  );\n}\n```\n\n**Why this works:**\n\n1. **State-driven animation**: Every `animate` prop is a pure function of props (`focused`, `fixing`, `reloading`, etc.). No imperative animation calls.\n2. **Layered motion**: The component has 4 independent motion layers (container, content, overlay, bottom panel) that animate based on the same state but with different transitions.\n3. **Physics consistency**: Spring parameters come from a shared `config` object, ensuring all browser mockups in the app feel the same.\n4. **Sequenced loading**: The progress bar uses 4 distinct motion states (loading → loaded → reloading → reloadDone) with precise timing to tell a story.\n5. **Performance**: All animations use `transform` and `opacity` only. The overlay uses a very low opacity (0.015) to avoid visual heaviness while still providing focus indication.\n\n## Anti-Patterns\n\n### ❌ Animating Expensive Properties\n\n```tsx\n\u002F\u002F BAD — triggers layout recalculation every frame\n\u003Cmotion.div animate={{ width: 100, height: 100, left: 200, top: 200 }} \u002F>\n\n\u002F\u002F GOOD — use transform-only properties\n\u003Cmotion.div animate={{ scale: 1.5, x: 200, y: 200 }} \u002F>\n```\n\n**Always prefer:** `transform`, `opacity` over `width`, `height`, `top`, `left`, `margin`\n\n### ❌ Missing `key` in `AnimatePresence`\n\n```tsx\n\u002F\u002F BAD — AnimatePresence can't track elements\n\u003CAnimatePresence>\n  {items.map((item) => (\n    \u003Cmotion.div exit={{ opacity: 0 }}>{item}\u003C\u002Fmotion.div> \u002F\u002F ❌ no key\n  ))}\n\u003C\u002FAnimatePresence>\n\n\u002F\u002F GOOD\n\u003CAnimatePresence>\n  {items.map((item) => (\n    \u003Cmotion.div key={item.id} exit={{ opacity: 0 }}>\n      {item}\n    \u003C\u002Fmotion.div>\n  ))}\n\u003C\u002FAnimatePresence>\n```\n\n### ❌ Inline Object in `animate` Without Memoization\n\n```tsx\n\u002F\u002F BAD — new object every render, triggers re-animation\n\u003Cmotion.div animate={{ opacity: isVisible ? 1 : 0 }} \u002F>\n\n\u002F\u002F GOOD — use variants or memoize\nconst variants = { visible: { opacity: 1 }, hidden: { opacity: 0 } };\n\u003Cmotion.div variants={variants} animate={isVisible ? \"visible\" : \"hidden\"} \u002F>\n```\n\n### ❌ Overusing Spring for Simple Transitions\n\n```tsx\n\u002F\u002F BAD — spring for opacity feels sluggish\n\u003Cmotion.div\n  animate={{ opacity: 1 }}\n  transition={{ type: \"spring\", stiffness: 100 }}\n\u002F>\n\n\u002F\u002F GOOD — tween for opacity, spring for position\n\u003Cmotion.div\n  animate={{ opacity: 1, y: 0 }}\n  transition={{\n    opacity: { duration: 0.3 },\n    y: { type: \"spring\", stiffness: 300 },\n  }}\n\u002F>\n```\n\n### ❌ Animating During Scroll Without `useScroll`\n\n```tsx\n\u002F\u002F BAD — manual scroll listener + setState = jank\nuseEffect(() => {\n  const handleScroll = () => setScrollY(window.scrollY);\n  window.addEventListener(\"scroll\", handleScroll);\n}, []);\n\n\u002F\u002F GOOD — useScroll hook (optimized, RAF-driven)\nconst { scrollY } = useScroll();\nconst scale = useTransform(scrollY, [0, 500], [1, 0.8]);\n\u003Cmotion.div style={{ scale }} \u002F>\n```\n\n### ❌ Nested `motion` Components Without `layout`\n\n```tsx\n\u002F\u002F BAD — parent moves but children jump\n\u003Cmotion.div animate={{ x: 100 }}>\n  \u003Cdiv>Child jumps\u003C\u002Fdiv>\n\u003C\u002Fmotion.div>\n\n\u002F\u002F GOOD — use layout for smooth child transitions\n\u003Cmotion.div animate={{ x: 100 }}>\n  \u003Cmotion.div layout>Child animates smoothly\u003C\u002Fmotion.div>\n\u003C\u002Fmotion.div>\n```\n\n### ❌ Heavy Computation in `useTransform`\n\n```tsx\n\u002F\u002F BAD — runs on every frame\nconst value = useTransform(scrollY, (v) => expensiveCalculation(v));\n\n\u002F\u002F GOOD — pre-compute or use useMotionValueEvent\nconst value = useTransform(scrollY, [0, 1000], [0, 1]);\n```\n\n## Best Practices\n\n### 1. Define Variants Outside Components\n\n```tsx\n\u002F\u002F ✅ Defined once, reused, no re-renders\nconst cardVariants = {\n  rest: { scale: 1 },\n  hover: { scale: 1.02 },\n};\n\nfunction Card() {\n  return (\n    \u003Cmotion.div variants={cardVariants} initial=\"rest\" whileHover=\"hover\">\n      Content\n    \u003C\u002Fmotion.div>\n  );\n}\n```\n\n### 2. Use `will-change` Sparingly\n\n```tsx\n\u002F\u002F ✅ Only on actively animating elements\n\u003Cmotion.div\n  style={{ willChange: \"transform\" }}\n  animate={{ x: 100 }}\n\u002F>\n```\n\n### 3. Respect `prefers-reduced-motion`\n\n```tsx\nconst prefersReducedMotion = useReducedMotion();\n\n\u003Cmotion.div\n  animate={prefersReducedMotion ? {} : { x: 100 }}\n\u002F>\n```\n\n### 4. Lazy Load Heavy Animations\n\n```tsx\nimport { lazy, Suspense } from \"react\";\n\nconst HeavyAnimation = lazy(() => import(\".\u002FHeavyAnimation\"));\n\n\u003CSuspense fallback={\u003Cdiv>Loading...\u003C\u002Fdiv>}>\n  \u003CHeavyAnimation \u002F>\n\u003C\u002FSuspense>\n```\n\n### 5. Use `layoutId` for Shared Element Transitions\n\n```tsx\n\u002F\u002F Card on page A\n\u003Cmotion.div layoutId=\"card\" className=\"w-20 h-20 bg-blue-500\" \u002F>\n\n\u002F\u002F Expanded card on page B\n\u003Cmotion.div layoutId=\"card\" className=\"w-full h-64 bg-blue-500\" \u002F>\n```\n\n## Performance Checklist\n\n- [ ] Use `transform` and `opacity` only when possible\n- [ ] Add `will-change: transform` to actively animating elements\n- [ ] Use `useReducedMotion()` for accessibility\n- [ ] Avoid animating `box-shadow` and `blur` simultaneously\n- [ ] Use `layout` prop only when necessary (has overhead)\n- [ ] Memoize variant objects defined in components\n- [ ] Use `AnimatePresence` `mode=\"popLayout\"` for lists\n- [ ] Prefer `useScroll` over manual scroll listeners\n\n## Summary\n\n| Pattern | Do | Don't |\n|---------|-----|-------|\n| Properties | `transform`, `opacity` | `width`, `height`, `top`, `margin` |\n| Transitions | Spring for movement, tween for fades | Spring for opacity changes |\n| Lists | `AnimatePresence` + `layout` + stable `key` | Missing keys, no exit animations |\n| Scroll | `useScroll`, `useInView` | Manual `scroll` listeners + `setState` |\n| Gestures | `whileHover`, `whileTap`, `whileDrag` | Manual mouse event handlers |\n| Shared elements | `layoutId` | Manual position calculation |\n| Accessibility | `useReducedMotion()` | Ignore motion preferences |\n",{"name":5,"license":14,"description":7,"compatibility":28},"main","816c073aaf171300af3605b06f7ec5aa7370ef0b","reference\u002Fghui\u002F.opencode\u002Fskills\u002Freact\u002Fanimation","https:\u002F\u002Fgithub.com\u002Flambda-solver\u002Flambdabench-pro","https:\u002F\u002Fgithub.com\u002Flambda-solver","2026-05-31 17:08:46",{"source":8,"skillName":5,"commands":49,"prompt":59,"zipAvailable":60},[50,53,56],{"tool":51,"command":52},"npx","npx skills add lambda-solver\u002Flambdabench-pro --skill react-animation",{"tool":54,"command":55},"bunx","bunx skills add lambda-solver\u002Flambdabench-pro --skill react-animation",{"tool":57,"command":58},"pnpm","pnpm dlx skills add lambda-solver\u002Flambdabench-pro --skill react-animation","安装技能，技能地址：https:\u002F\u002Fgithub.com\u002Flambda-solver\u002Flambdabench-pro\u002Fblob\u002Fmain\u002Freference\u002Fghui\u002F.opencode\u002Fskills\u002Freact\u002Fanimation\u002FSKILL.md",true,[],[63,91,109,128,148,167],{"skillNo":64,"name":65,"nameZh":6,"description":66,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":6,"repositoryNo":12,"starsCount":13,"licenseSpdxId":14,"licenseFileUrl":6,"sourceHtmlUrl":67,"categories":68,"keywords":72,"publishedAt":37},"01M71H9FNA5D6Y4ANXD18Z1Z3X","tmux-workflow","Development workflow with tmux — run dev servers, Storybook, and background processes in persistent sessions with live logs, while keeping OpenCode free for chat","https:\u002F\u002Fgithub.com\u002Flambda-solver\u002Flambdabench-pro\u002Fblob\u002Fmain\u002Freference\u002Fghui\u002F.opencode\u002Fskills\u002Ftmux\u002FSKILL.md",[69],{"categoryNo":70,"categoryName":71,"taxonomyType":20,"taxonomyTypeDesc":21},"01JBSEEDCAT000000000000023","Skill与智能体开发",[73,74,75,76,77,78,79,80,28,36,81,82,83,84,85,86,87,88,89,90],"tmux","workflow","tmux workflow","dev servers","free","live","servers","run","chat","logs","development workflow","storybook","dev","sessions","for","development","processes","background",{"skillNo":92,"name":93,"nameZh":6,"description":94,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":6,"repositoryNo":12,"starsCount":13,"licenseSpdxId":14,"licenseFileUrl":6,"sourceHtmlUrl":95,"categories":96,"keywords":98,"publishedAt":37},"01M71H9DQJDAJVZX37NDN99D3P","04-anti-patterns","React anti-patterns to avoid — manual JSX repetition, useEffect misuse, reference instability, prop drilling, conditional hooks, and common performance traps","https:\u002F\u002Fgithub.com\u002Flambda-solver\u002Flambdabench-pro\u002Fblob\u002Fmain\u002Freference\u002Fghui\u002F.opencode\u002Fskills\u002Freact\u002Fpatterns\u002F04-anti-patterns\u002FSKILL.md",[97],{"categoryNo":18,"categoryName":19,"taxonomyType":20,"taxonomyTypeDesc":21},[99,100,101,102,103,104,105,106,26,107,28,108,24],"patterns","anti patterns","04 anti patterns","jsx","reference","conditional","hooks","manual","anti-patterns","common",{"skillNo":110,"name":111,"nameZh":6,"description":112,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":6,"repositoryNo":12,"starsCount":13,"licenseSpdxId":14,"licenseFileUrl":6,"sourceHtmlUrl":113,"categories":114,"keywords":116,"publishedAt":37},"01M71H9BXF511W2R3B73S5P9ZN","03-state-management","React state management patterns — lifting state, colocation, URL state, server state with TanStack Query, local state with useState\u002FuseReducer","https:\u002F\u002Fgithub.com\u002Flambda-solver\u002Flambdabench-pro\u002Fblob\u002Fmain\u002Freference\u002Fghui\u002F.opencode\u002Fskills\u002Freact\u002Fpatterns\u002F03-state-management\u002FSKILL.md",[115],{"categoryNo":18,"categoryName":19,"taxonomyType":20,"taxonomyTypeDesc":21},[117,118,119,120,24,121,99,122,123,124,125,28,126,36,127],"management","state management","state","03 state management","query","URL","tanstack","react state management","tanstack query","server","local",{"skillNo":129,"name":130,"nameZh":6,"description":131,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":6,"repositoryNo":12,"starsCount":13,"licenseSpdxId":14,"licenseFileUrl":6,"sourceHtmlUrl":132,"categories":133,"keywords":135,"publishedAt":37},"01M71H9AAGVWMYQ4NG82QPYCTE","02-component-composition","React component composition patterns — render props, compound components, list rendering with map, slot patterns, and layout primitives","https:\u002F\u002Fgithub.com\u002Flambda-solver\u002Flambdabench-pro\u002Fblob\u002Fmain\u002Freference\u002Fghui\u002F.opencode\u002Fskills\u002Freact\u002Fpatterns\u002F02-component-composition\u002FSKILL.md",[134],{"categoryNo":18,"categoryName":19,"taxonomyType":20,"taxonomyTypeDesc":21},[136,137,138,36,28,139,24,140,99,141,142,143,144,145,146,29,147],"02 component composition","component","Composition","components","primitives","composition patterns","list","map","rendering","render","react component","compound",{"skillNo":149,"name":150,"nameZh":6,"description":151,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":6,"repositoryNo":12,"starsCount":13,"licenseSpdxId":14,"licenseFileUrl":6,"sourceHtmlUrl":152,"categories":153,"keywords":155,"publishedAt":37},"01M71H98RDXRQZDR496SRD906M","01-fp-style","React functional programming style — pure components, hooks conventions, Effect Atom integration, and Tailwind patterns","https:\u002F\u002Fgithub.com\u002Flambda-solver\u002Flambdabench-pro\u002Fblob\u002Fmain\u002Freference\u002Fghui\u002F.opencode\u002Fskills\u002Freact\u002Fpatterns\u002F01-fp-style\u002FSKILL.md",[154],{"categoryNo":18,"categoryName":19,"taxonomyType":20,"taxonomyTypeDesc":21},[156,157,158,99,159,160,28,161,24,162,139,105,163,164,165,166],"01 fp style","style","functional","conventions","integration","atom","programming","Tailwind","tailwind patterns","functional programming","effect",{"skillNo":168,"name":169,"nameZh":6,"description":170,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":6,"repositoryNo":12,"starsCount":13,"licenseSpdxId":14,"licenseFileUrl":6,"sourceHtmlUrl":171,"categories":172,"keywords":177,"publishedAt":37},"01M71H92TG8T2E5ZQ95PHAKZZC","04-bun-test","Bun built-in test runner — zero-config testing for Effect, React, and TypeScript projects","https:\u002F\u002Fgithub.com\u002Flambda-solver\u002Flambdabench-pro\u002Fblob\u002Fmain\u002Freference\u002Fghui\u002F.opencode\u002Fskills\u002Feffect-ts\u002Ftesting\u002F04-bun-test\u002FSKILL.md",[173,176],{"categoryNo":174,"categoryName":175,"taxonomyType":20,"taxonomyTypeDesc":21},"01JBSEEDCAT000000000000008","测试与质量",{"categoryNo":18,"categoryName":19,"taxonomyType":20,"taxonomyTypeDesc":21},[178,179,180,181,182,166,183,28,184,185,186,187,87,188,24],"bun test","04 bun test","bun","test","projects","runner","config","typescript","test runner","ZeRO","testing"]