--- name: animation-motion description: Create smooth animations and micro-interactions with Framer Motion and CSS. Covers enter/exit animations, gestures, scroll animations, loading states, and performance optimization. Use for polished UIs, interactive elements, and engaging user experiences. --- # Animation & Motion Design Create smooth, purposeful animations that enhance user experience. ## Instructions 1. **Animate with purpose** - Motion should guide, not distract 2. **Keep it fast** - Most UI animations should be 150-300ms 3. **Use easing curves** - Never use linear timing for UI 4. **Respect preferences** - Honor `prefers-reduced-motion` 5. **Optimize performance** - Animate `transform` and `opacity` only ## Framer Motion (Recommended) ### Setup ```bash npm install framer-motion ``` ### Basic Animations ```tsx import { motion } from 'framer-motion'; // Fade in on mount function FadeIn({ children }: { children: React.ReactNode }) { return ( {children} ); } // Slide up on mount function SlideUp({ children }: { children: React.ReactNode }) { return ( {children} ); } // Scale on hover function ScaleButton({ children }: { children: React.ReactNode }) { return ( {children} ); } ``` ### Enter/Exit Animations ```tsx import { motion, AnimatePresence } from 'framer-motion'; function Modal({ isOpen, onClose, children }: ModalProps) { return ( {isOpen && ( <> {/* Backdrop */} {/* Modal */}
{children}
)}
); } ``` ### List Animations ```tsx import { motion, AnimatePresence } 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 AnimatedList({ items }: { items: Item[] }) { return ( {items.map((item) => ( ))} ); } ``` ### Layout Animations ```tsx import { motion, LayoutGroup } from 'framer-motion'; function ExpandableCard({ id, title, content, isExpanded, onToggle }: Props) { return ( {title} {isExpanded && (

{content}

)}
); } ``` ### Scroll Animations ```tsx import { motion, useScroll, useTransform } from 'framer-motion'; function ParallaxHero() { const { scrollY } = useScroll(); // Parallax effect - image moves slower than scroll const y = useTransform(scrollY, [0, 500], [0, 150]); const opacity = useTransform(scrollY, [0, 300], [1, 0]); return (

Welcome

); } // Reveal on scroll function ScrollReveal({ children }: { children: React.ReactNode }) { return ( {children} ); } ``` ### Gesture Animations ```tsx import { motion, useDragControls } from 'framer-motion'; function DraggableCard() { return ( ); } function SwipeToDelete({ onDelete }: { onDelete: () => void }) { return ( { if (info.offset.x < -100) { onDelete(); } }} className="bg-white p-4 rounded-lg" > Swipe left to delete ); } ``` ## Loading States ### Skeleton Loader ```tsx function Skeleton({ className = '' }: { className?: string }) { return (
); } function CardSkeleton() { return (
); } ``` ### Spinner ```tsx function Spinner({ size = 'md' }: { size?: 'sm' | 'md' | 'lg' }) { const sizes = { sm: 'w-4 h-4', md: 'w-8 h-8', lg: 'w-12 h-12', }; return ( ); } ``` ### Progress Bar ```tsx function ProgressBar({ value, max = 100 }: { value: number; max?: number }) { const percentage = Math.min((value / max) * 100, 100); return (
); } ``` ## CSS Animations ### Tailwind Animations ```tsx // Built-in Tailwind animations
Spinner
Ping
Pulse
Bounce
// Custom animation in tailwind.config.js module.exports = { theme: { extend: { animation: { 'fade-in': 'fadeIn 0.3s ease-out', 'slide-up': 'slideUp 0.4s ease-out', 'scale-in': 'scaleIn 0.2s ease-out', }, keyframes: { fadeIn: { '0%': { opacity: '0' }, '100%': { opacity: '1' }, }, slideUp: { '0%': { transform: 'translateY(20px)', opacity: '0' }, '100%': { transform: 'translateY(0)', opacity: '1' }, }, scaleIn: { '0%': { transform: 'scale(0.95)', opacity: '0' }, '100%': { transform: 'scale(1)', opacity: '1' }, }, }, }, }, }; ``` ### CSS Transitions ```tsx ``` ## Respecting User Preferences ```tsx import { useReducedMotion } from 'framer-motion'; function AnimatedComponent() { const shouldReduceMotion = useReducedMotion(); return ( Content ); } // CSS approach @media (prefers-reduced-motion: reduce) { *, *::before, *::after { animation-duration: 0.01ms !important; transition-duration: 0.01ms !important; } } ``` ## Performance Tips 1. **Animate only transform and opacity** - GPU accelerated 2. **Use `will-change` sparingly** - Only for complex animations 3. **Avoid layout thrashing** - Don't animate width/height 4. **Use `layout` prop carefully** - Can cause reflows 5. **Debounce scroll handlers** - Prevent jank ```tsx // Good - GPU accelerated // Bad - causes reflow ``` ## When to Use - Page transitions and navigation - Loading and skeleton states - Interactive UI elements - Feedback and confirmations - Onboarding and tutorials - Data visualization transitions ## Notes - Framer Motion adds ~30kb to bundle (gzipped) - Use CSS for simple hover/focus transitions - Test animations at 0.25x speed to verify smoothness - Consider motion sickness - avoid excessive movement