ワンクリックで
framer-motion
Framer Motion / Motion sub-skill - AnimatePresence, layout animations, gestures, motion values.
Codex または Claude でインストール この Prompt をコピーして Codex、Claude、または他のアシスタントに貼り付けると、Skill ページを確認してインストールできます。
メニュー
Framer Motion / Motion sub-skill - AnimatePresence, layout animations, gestures, motion values.
Codex または Claude でインストール この Prompt をコピーして Codex、Claude、または他のアシスタントに貼り付けると、Skill ページを確認してインストールできます。
SOC 職業分類に基づく
Cast genjutsu on a UI - creative coding for motion, micro-interactions, and wow-factor. Scans the stack, proposes an interaction thesis, loads the right sub-skills, implements the illusion. Adapts to Web, Android (Compose), Apple (SwiftUI).
Paint a complete visual universe with genjutsu - art direction brainstorm, design system, implementation, audit. Anti-AI-slop design pipeline. Adapts to Web, Android (Compose), Apple (SwiftUI).
Algorithmic and generative art with Canvas 2D - particles, flow fields, noise, fractals, L-systems.
Advanced Compose visuals - Material 3 Expressive motion physics, AGSL shaders (Android 13+), Canvas/DrawScope generative, graphicsLayer effects.
Jetpack Compose animation foundations - animate*AsState, AnimatedVisibility, Crossfade, updateTransition, SharedTransitionLayout, gestures.
Compose Multiplatform / KMP patterns - expect/actual composables, platform-specific code, density and font handling cross-target, iOS/Android/Desktop interop.
| name | framer-motion |
| description | Framer Motion / Motion sub-skill - AnimatePresence, layout animations, gestures, motion values. |
Package:
motion(v11+, formerlyframer-motion). Import:import { motion, AnimatePresence } from "motion/react"
| Criteria | Framer Motion | GSAP | Native CSS |
|---|---|---|---|
| Layout animations | Excellent (layoutId) | Manual | Impossible |
| Exit animations | AnimatePresence | Timeline reverse | Limited (display) |
| Gestures (drag, hover) | Native, declarative | Draggable plugin | Basic |
| Scroll-driven | useScroll + useTransform | ScrollTrigger (more powerful) | scroll-timeline |
| Complex orchestration | Variants + propagation | Timeline (more flexible) | @keyframes |
| Bundle size | ~50kb tree-shaken | ~30kb core | 0kb |
| React integration | Native, component-first | Refs + useGSAP | className toggle |
Rule: Framer Motion for React UI interactions (modals, toasts, reorder, shared layout). GSAP for complex timelines, cinematic scroll-driven, SVG morphing.
<AnimatePresence mode="wait">
{isVisible && (
<motion.div
key="unique-key" // REQUIRED — identifies the component
initial={{ opacity: 0 }}
animate={{ opacity: 1 }}
exit={{ opacity: 0 }}
/>
)}
</AnimatePresence>
mode="wait" — waits for exit to finish before enter (page transitions)mode="sync" — exit and enter simultaneouslymode="popLayout" — removes from flow immediately (good for lists)onExitComplete — callback when all exit animations are finished// Shared layout — the element "slides" between two positions
<motion.div layoutId="highlight" className={activeTab === id ? "active" : ""} />
// Auto layout — animates position/size when layout changes
<motion.div layout>
{isExpanded && <motion.p layout>Additional content</motion.p>}
</motion.div>
// layout="position" — animates position only (not size)
// layout="size" — animates size only
// layout="preserve-aspect" — preserves the ratio during the transition
const container = {
hidden: { opacity: 0 },
show: {
opacity: 1,
transition: {
staggerChildren: 0.08,
delayChildren: 0.2,
staggerDirection: 1, // 1 = normal, -1 = reverse
},
},
};
const item = {
hidden: { opacity: 0, y: 20 },
show: { opacity: 1, y: 0 },
};
<motion.ul variants={container} initial="hidden" animate="show">
{items.map((i) => (
<motion.li key={i.id} variants={item} />
))}
</motion.ul>
Variants automatically propagate to motion children — no need for initial/animate on children.
<motion.div
whileHover={{ scale: 1.05 }}
whileTap={{ scale: 0.95 }}
whileFocus={{ borderColor: "#3b82f6" }}
// Drag
drag // true = x+y, "x" = horizontal only, "y" = vertical only
dragConstraints={{ left: -100, right: 100, top: -50, bottom: 50 }}
dragElastic={0.2} // 0 = rigid, 1 = free (default 0.35)
dragSnapToOrigin // returns to initial position
onDragEnd={(e, info) => {
if (info.offset.x > 100) handleSwipe("right");
}}
/>
const x = useMotionValue(0);
const opacity = useTransform(x, [-200, 0, 200], [0, 1, 0]);
const background = useTransform(x, [-200, 200], ["#ff0000", "#00ff00"]);
// Spring-based smoothing
const smoothX = useSpring(x, { stiffness: 300, damping: 30 });
// Scroll tracking
const { scrollY, scrollYProgress } = useScroll();
const parallaxY = useTransform(scrollYProgress, [0, 1], [0, -300]);
// Element-scoped scroll
const ref = useRef(null);
const { scrollYProgress } = useScroll({
target: ref,
offset: ["start end", "end start"],
});
Motion values do NOT trigger React re-renders — they update the DOM directly via style.
// BAD — infinite re-render if animate depends on state
onUpdate={(latest) => setPosition(latest.x)}
// GOOD — guard or useMotionValueEvent
const x = useMotionValue(0);
useMotionValueEvent(x, "change", (latest) => {
if (latest > threshold) onThresholdReached();
});
// BAD — key changes every render, breaks layout tracking
<motion.div layout key={Math.random()} />
// GOOD — stable key derived from data
<motion.div layout key={item.id} />
// BAD — no key, exit animation does not trigger
<AnimatePresence>
{isOpen && <motion.div exit={{ opacity: 0 }} />}
</AnimatePresence>
// GOOD — unique key for each conditional child
<AnimatePresence>
{isOpen && <motion.div key="modal" exit={{ opacity: 0 }} />}
</AnimatePresence>
// BAD — double animation, transform conflicts
<motion.div animate={{ x: 100 }}>
<motion.div animate={{ x: -50 }}>Content</motion.div>
</motion.div>
// GOOD — single animation level per transform axis
<motion.div animate={{ x: 100 }}>
<motion.div animate={{ opacity: 0.5 }}>Content</motion.div>
</motion.div>
// GOOD — use variants to coordinate parent/child
<motion.div variants={parent} animate="active">
<motion.div variants={child} />
</motion.div>