---
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