Skip to main content

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

Source Affaan M
License MIT
First documented
Receipts TODO

Trigger phrases

Phrases that activate this skill when typed to Claude Code:

  • animate this modal open
  • framer-motion button feedback
  • shared 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-video or remotion-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

  1. 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.
  2. Pick a duration token. fast for button feedback, normal for modal / card, slow for hero entrance. Imported from motionTokens, no inline values.
  3. Pick a spring or easing. snappy for default UI, gentle for cards / modals, instant for popovers. Springs go in the transition object’s type: "spring"; for non-spring transitions, motionTokens.easing.smooth for natural motion, motionTokens.easing.sharp for snappy / mechanical feel.
  4. Wire initial + animate + exit. Use only transform and opacity — layout properties are banned by foundations. AnimatePresence wraps the conditionally-rendered component for exit animations.
  5. Reduced-motion fallback. If useReducedMotion() is true, transforms get disabled; only opacity fades ≤ 0.2s are allowed.
  6. 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.