motion-ui
A Claude Code skill from Affaan M's everything-claude-code repo for the React / Next.js UI motion system layer — buttons, modals, menus, state transitions, navigation continuity — built on motion/react with mandatory reduced-motion support and device adaptation. Consumes motion-foundations tokens and springs.
Build interactive component motion (buttons, modals, menus, state transitions) on top of the motion-foundations layer
Trigger phrases
Phrases that activate this skill when typed to Claude Code:
animate this modal openframer-motion button feedbackshared element transition for navigation
What it does
motion-ui is the component-layer motion skill in Affaan M’s everything-claude-code — see skills/motion-ui. It builds on motion-foundations (tokens + springs + shouldAnimate() gate + reduced-motion handling) and focuses on production component motion: buttons, modals, menus, state transitions (loading → loaded, open → closed), and navigation continuity (shared elements, crossfade).
Three principles repeated from foundations because they apply at the component layer too: motion must guide attention OR communicate state OR preserve spatial continuity, accessibility (reduced motion) is mandatory not optional, device adaptation (low-end fallback) is part of the contract. The skill is explicit about the package boundary — motion/react is the default for current Motion-for-React projects; framer-motion is the legacy import path for projects that still depend on it. Mixing the two causes conflicting internal schedulers and broken AnimatePresence contexts so components from one package won’t coordinate exit animations with the other.
The token bridge is concrete: components import motionTokens from @/lib/motionTokens and use motionTokens.duration.normal + motionTokens.easing.smooth + motionTokens.distance.md directly in transition and initial / animate props. Inline numeric durations or hardcoded easing tuples are banned by the foundations layer — motion-ui enforces consumption from the shared object. The “avoid using motion when” list is the negative space: purely decorative, reduces usability, impacts performance — remove instead of optimizing.
When to use it
- Interactive components (buttons with press feedback, modals opening / closing, menus expanding)
- State transitions (loading → loaded, open → closed, success / error states)
- Navigation continuity — shared elements between routes, crossfade page transitions
- Layout-change animations where preserving spatial continuity matters
- Form interaction motion (focus rings, error shake, submit success)
When not to reach for it:
- Setting up the base layer (tokens / springs / reduced-motion) — that’s
motion-foundations - Native iOS / macOS animations — that’s
swiftui-patterns/liquid-glass-design - Video animation primitives — that’s
manim-videoorremotion-video-creation - Decorative motion with no UX purpose — remove instead of building
Install
From affaan-m/everything-claude-code at skills/motion-ui/. Drop the folder into ~/.claude/skills/motion-ui/. Runtime needs motion (the package; imports from motion/react) — npm install motion per the upstream. Optional framer-motion for legacy projects, but the skill is explicit that mixing the two in the same project is a bug. Depends on motion-foundations being in the project first (the motionTokens / springs / shouldAnimate() references all assume the foundations layer landed).
What a session looks like
- Pick the motion purpose. Modal open = communicate state. Button press = communicate state + guide attention. Tab switch = preserve spatial continuity. If none, the skill says remove the motion.
- Pick a duration token.
fastfor button feedback,normalfor modal / card,slowfor hero entrance. Imported frommotionTokens, no inline values. - Pick a spring or easing.
snappyfor default UI,gentlefor cards / modals,instantfor popovers. Springs go in thetransitionobject’stype: "spring"; for non-spring transitions,motionTokens.easing.smoothfor natural motion,motionTokens.easing.sharpfor snappy / mechanical feel. - Wire
initial+animate+exit. Use onlytransformandopacity— layout properties are banned by foundations.AnimatePresencewraps the conditionally-rendered component for exit animations. - Reduced-motion fallback. If
useReducedMotion()is true, transforms get disabled; only opacity fades ≤ 0.2s are allowed. - Test across devices. Low-end-device gate from foundations covers the perf fallback. The skill’s stance: responsiveness > smoothness.
The discipline that makes it work: tokens-as-source-of-truth. Once a component imports motionTokens.duration.normal, fixing the duration for the whole design system is one edit to the foundations object. Without that, “make the modals snappier” becomes a global find-replace across every motion file.
Receipts
TODO — to be filled in from a real session. Once the motion system has been wired into a real production app, this section will capture: which spring preset (snappy / gentle / bouncy / instant / release) got the most use vs. which were defined and never picked, how often the layout-property ban (no width / height / top / margin / padding) surfaced as a fight with a designer’s spec, whether motion/react vs. framer-motion package coexistence actually broke AnimatePresence as the skill warns or stayed stable, and the actual frame-rate impact of reduced motion on the worst case animation in the app.
Source and attribution
From Affaan M’s everything-claude-code — an MIT-licensed skill collection covering harness construction, agent ops, video, payments, and platform-specific patterns.
License: MIT.
Quoting the package-mixing warning verbatim: “Do not mix. Mixing causes conflicting internal schedulers and broken AnimatePresence contexts.” That’s the wedge — most “framer motion is broken” reports trace back to two packages in the same dependency graph; the skill enforces single-source imports as the first thing to check.