animation-motion
Animation & Motion Design
Create smooth, purposeful animations that enhance user experience.
Instructions
- Animate with purpose - Every animation should serve a function
- Keep it subtle - 200-400ms for most UI transitions
- Respect reduced motion - Honor user preferences
- Optimize performance - Use transform and opacity
- Use consistent easing - Create a motion language
Framer Motion Basics
Simple Animations
import { motion } from 'framer-motion';
// Fade in on mount
<motion.div
initial={{ opacity: 0 }}
animate={{ opacity: 1 }}
transition={{ duration: 0.3 }}
>
Content
</motion.div>
// Slide up with spring
<motion.div
initial={{ y: 20, opacity: 0 }}
animate={{ y: 0, opacity: 1 }}
transition={{
type: 'spring',
stiffness: 300,
damping: 30,
}}
>
Content
</motion.div>
Exit Animations
import { AnimatePresence, motion } from 'framer-motion';
function Modal({ isOpen, onClose, children }) {
return (
<AnimatePresence>
{isOpen && (
<>
{/* Backdrop */}
<motion.div
className="fixed inset-0 bg-black/50"
initial={{ opacity: 0 }}
animate={{ opacity: 1 }}
exit={{ opacity: 0 }}
onClick={onClose}
/>
{/* Modal content */}
<motion.div
className="fixed inset-0 flex items-center justify-center"
initial={{ opacity: 0, scale: 0.95 }}
animate={{ opacity: 1, scale: 1 }}
exit={{ opacity: 0, scale: 0.95 }}
transition={{ duration: 0.2 }}
>
{children}
</motion.div>
</>
)}
</AnimatePresence>
);
}
Gesture Animations
import { motion } from 'framer-motion';
<motion.button
whileHover={{ scale: 1.05 }}
whileTap={{ scale: 0.95 }}
transition={{ type: 'spring', stiffness: 400, damping: 17 }}
>
Click me
</motion.button>
// Drag
<motion.div
drag
dragConstraints={{ left: 0, right: 300, top: 0, bottom: 300 }}
dragElastic={0.1}
whileDrag={{ scale: 1.1 }}
>
Drag me
</motion.div>
List Animations
import { motion } from 'framer-motion';
const container = {
hidden: { opacity: 0 },
show: {
opacity: 1,
transition: {
staggerChildren: 0.1,
},
},
};
const item = {
hidden: { opacity: 0, y: 20 },
show: { opacity: 1, y: 0 },
};
function List({ items }) {
return (
<motion.ul
variants={container}
initial="hidden"
animate="show"
>
{items.map((data) => (
<motion.li key={data.id} variants={item}>
{data.title}
</motion.li>
))}
</motion.ul>
);
}
Layout Animations
import { motion, LayoutGroup } from 'framer-motion';
function Tabs({ tabs, activeTab, onTabChange }) {
return (
<LayoutGroup>
<div className="flex space-x-2">
{tabs.map((tab) => (
<button
key={tab.id}
onClick={() => onTabChange(tab.id)}
className="relative px-4 py-2"
>
{tab.label}
{activeTab === tab.id && (
<motion.div
layoutId="activeTab"
className="absolute inset-0 bg-blue-100 rounded-lg"
style={{ zIndex: -1 }}
transition={{ type: 'spring', stiffness: 500, damping: 30 }}
/>
)}
</button>
))}
</div>
</LayoutGroup>
);
}
Scroll Animations
import { motion, useScroll, useTransform } from 'framer-motion';
function ParallaxHero() {
const { scrollY } = useScroll();
const y = useTransform(scrollY, [0, 500], [0, 150]);
const opacity = useTransform(scrollY, [0, 300], [1, 0]);
return (
<motion.div
style={{ y, opacity }}
className="h-screen flex items-center justify-center"
>
<h1 className="text-6xl font-bold">Welcome</h1>
</motion.div>
);
}
// Scroll-triggered animation
import { useInView } from 'framer-motion';
function FadeInSection({ children }) {
const ref = useRef(null);
const isInView = useInView(ref, { once: true, margin: '-100px' });
return (
<motion.div
ref={ref}
initial={{ opacity: 0, y: 50 }}
animate={isInView ? { opacity: 1, y: 0 } : { opacity: 0, y: 50 }}
transition={{ duration: 0.5 }}
>
{children}
</motion.div>
);
}
CSS Animations
Keyframe Animations
/* Tailwind config */
animation: {
'fade-in': 'fadeIn 0.3s ease-out',
'slide-up': 'slideUp 0.3s ease-out',
'spin-slow': 'spin 3s linear infinite',
'pulse-subtle': 'pulse 2s ease-in-out infinite',
},
keyframes: {
fadeIn: {
'0%': { opacity: '0' },
'100%': { opacity: '1' },
},
slideUp: {
'0%': { transform: 'translateY(10px)', opacity: '0' },
'100%': { transform: 'translateY(0)', opacity: '1' },
},
}
Transition Classes
// Hover transitions
<button className="
transition-all duration-200 ease-out
hover:scale-105 hover:shadow-lg
active:scale-95
">
Click me
</button>
// Color transitions
<div className="
transition-colors duration-300
bg-gray-100 hover:bg-gray-200
dark:bg-gray-800 dark:hover:bg-gray-700
">
Content
</div>
Loading States
Skeleton Loading
function SkeletonCard() {
return (
<div className="animate-pulse">
<div className="h-48 bg-gray-200 rounded-lg" />
<div className="mt-4 space-y-3">
<div className="h-4 bg-gray-200 rounded w-3/4" />
<div className="h-4 bg-gray-200 rounded w-1/2" />
</div>
</div>
);
}
Spinner Component
function Spinner({ size = 'md' }) {
const sizes = {
sm: 'w-4 h-4',
md: 'w-6 h-6',
lg: 'w-8 h-8',
};
return (
<svg
className={`animate-spin ${sizes[size]} text-blue-600`}
xmlns="http://www.w3.org/2000/svg"
fill="none"
viewBox="0 0 24 24"
>
<circle
className="opacity-25"
cx="12"
cy="12"
r="10"
stroke="currentColor"
strokeWidth="4"
/>
<path
className="opacity-75"
fill="currentColor"
d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4z"
/>
</svg>
);
}
Reduced Motion Support
import { useReducedMotion } from 'framer-motion';
function AnimatedComponent() {
const shouldReduceMotion = useReducedMotion();
return (
<motion.div
initial={{ opacity: 0, y: shouldReduceMotion ? 0 : 20 }}
animate={{ opacity: 1, y: 0 }}
transition={{ duration: shouldReduceMotion ? 0 : 0.3 }}
>
Content
</motion.div>
);
}
// CSS approach
@media (prefers-reduced-motion: reduce) {
* {
animation-duration: 0.01ms !important;
animation-iteration-count: 1 !important;
transition-duration: 0.01ms !important;
}
}
Performance Tips
- Animate transform and opacity only - GPU accelerated
- Use
will-changesparingly - For complex animations - Avoid layout thrashing - Don't animate width/height
- Use
AnimatePresencemode="wait" - Prevent animation overlap - Lazy load animations - For below-fold content
// GPU-optimized animation
<motion.div
animate={{ x: 100 }} // Good: transform
// animate={{ left: 100 }} // Bad: layout property
/>
// will-change for complex animations
<div style={{ willChange: 'transform' }}>
Heavy animation here
</div>
Best Practices
- 200-400ms for transitions - Feels responsive
- Spring for interactive elements - Natural feel
- Ease-out for enter - Elements arrive and settle
- Ease-in for exit - Elements accelerate away
- Stagger lists - 50-100ms between items
- Match motion to meaning - Slide for navigation, fade for content
When to Use
- Page transitions and navigation
- Modal and dialog animations
- Loading and progress states
- Micro-interactions and feedback
- Scroll-driven effects
- Interactive data visualizations
Notes
- Test on lower-end devices
- Always respect prefers-reduced-motion
- Keep animations consistent across the app
- Don't animate everything - be selective
More from housegarofalo/claude-code-base
home-assistant
Ultimate Home Assistant skill - complete administration, wireless protocols (Zigbee/ZHA/Z2M, Z-Wave JS, Thread, Matter), ESPHome device building, advanced troubleshooting, performance optimization, security hardening, custom integration development, and professional dashboard design. Covers configuration, REST API, automation debugging, database optimization, SSL/TLS, Jinja2 templating, and HACS custom cards. Use for any HA task.
6power-automate
Expert guidance for Power Automate development including cloud flows, desktop flows, Dataverse connector, expression functions, custom connectors, error handling, and child flow patterns. Use when building automated workflows, writing flow expressions, creating custom connectors from OpenAPI, or implementing error handling patterns.
5mobile-pwa
Build Progressive Web Apps with offline support, push notifications, and native-like experiences. Covers service workers, Web App Manifest, caching strategies, IndexedDB, background sync, and installability. Use for mobile-first web apps, offline-capable applications, and app-like experiences.
5matter-thread
>
5tanstack-query
Manage server state with TanStack Query (React Query). Covers data fetching, caching, mutations, optimistic updates, infinite queries, and prefetching. Use for API integration, server state management, and data synchronization in React applications.
5vitest
Fast Vite-native unit testing framework for JavaScript/TypeScript. ESM-first, Jest-compatible API, with instant HMR. Use for modern frontend testing, Vue/React/Svelte testing, or fast test execution. Triggers on vitest, vite test, vue test, svelte test.
5