animation-designer — Claude Code skill: Framer Motion animations
A skill that hands your agent ready-made web animation patterns: page transitions, component reveals, parallax and micro-interactions.
npx skills add daffy0208/ai-dev-standards --skill animation-designer -a claude-codeWhat animation-designer does
This skill gives the agent concrete Framer Motion recipes instead of improvising. It covers component enter and exit animations, staggered lists, hover and tap feedback on buttons, and modals built with AnimatePresence. Page transitions for Next.js are included too, keyed on the current pathname.
Scroll work is a separate section: progress-driven reveals with useScroll and useTransform, a parallax background, and a reading progress bar. Loading states come as a spinner, a skeleton loader and pulsing dots. Everything is TypeScript and React, written to be pasted into a project.
You need the package installed first: npm install framer-motion. The patterns assume Framer Motion specifically, so a project already committed to another animation library means rewriting the examples by hand.
What it helps with
- make cards fade in when the page loads
- stagger a list so items appear one after another
- add a page transition in a Next.js app
- attach a parallax effect to a hero image
- show a skeleton loader while data is fetching
Example prompts for the agent
- Add a Framer Motion fade-in for the product cards
- Stagger the task list so each item appears with a delay
- Set up a page transition in Next.js with a slide effect
How to install animation-designer
1Skills CLI (Vercel)
npx skills add daffy0208/ai-dev-standards --skill animation-designer -a claude-code2Manually: put the files in place
~/.claude/skills/animation-designer/SKILL.mdCopy the whole skill folder; for one project use .claude/skills/animation-designer/
1Skills CLI (Vercel)
npx skills add daffy0208/ai-dev-standards --skill animation-designer -a codex2Manually: put the files in place
.agents/skills/animation-designer/SKILL.mdFor all projects use ~/.agents/skills/
1Skills CLI (Vercel)
npx skills add daffy0208/ai-dev-standards --skill animation-designer -a cursor2Manually: put the files in place
.cursor/skills/animation-designer/SKILL.mdCursor also reads .agents/skills/ and .claude/skills/
1GitHub CLI
gh skill install daffy0208/ai-dev-standards animation-designer2Manually: put the files in place
.github/skills/animation-designer/SKILL.md1Skills CLI (Vercel)
npx skills add daffy0208/ai-dev-standards --skill animation-designer -a gemini-cli2Platform CLI
gemini skills install https://github.com/daffy0208/ai-dev-standards --path skills/animation-designerManually: put the files in place
.devin/skills/animation-designer/SKILL.mdThe legacy .windsurf/skills/ path still works
Skills CLI (Vercel)
npx skills add daffy0208/ai-dev-standards --skill animation-designer -a opencodeClaude.ai
Zip the folder and upload it in Settings → Capabilities → Skills
Step by step, with commands for each platform: How to install skills in Claude Code: five ways
Questions about animation-designer
How do I install animation-designer in Claude?
Run npx skills add daffy0208/ai-dev-standards --skill animation-designer -a claude-code. The skill lands in ~/.claude/skills/animation-designer/SKILL.md.
What does animation-designer do?
It guides the agent through writing Framer Motion and CSS animations: component reveals, page transitions, scroll effects, loading states and micro-interactions.
What do I need before using this skill?
The examples rely on the framer-motion package, so install it with npm install framer-motion. The code is TypeScript and React.
Source text
SKILL.md · MIT
---
name: animation-designer
description: Expert in web animations, transitions, and motion design using Framer Motion and CSS
version: 1.0.0
tags: [animation, framer-motion, css-animations, transitions, motion-design]
---
# Animation Designer Skill
I help you create smooth, professional animations for web applications using Framer Motion and CSS.
## What I Do
**UI Animations:**
- Page transitions
- Component enter/exit animations
- Hover effects, button interactions
- Loading animations
**Scroll Animations:**
- Parallax effects
- Scroll-triggered animations
- Progress indicators
**Micro-interactions:**
- Button press feedback
- Form field focus states
- Success/error animations
- Drag and drop feedback
## Framer Motion Basics
### Installation
```bash
npm install framer-motion
```
### Basic Animation
```typescript
import { motion } from 'framer-motion'
export function FadeIn({ children }: { children: React.ReactNode }) {
return (
<motion.div
initial={{ opacity: 0 }}
animate={{ opacity: 1 }}
transition={{ duration: 0.5 }}
>
{children}
</motion.div>
)
}
```
---
## Common Animation Patterns
### Pattern 1: Fade In on Mount
```typescript
import { motion } from 'framer-motion'
export function Card({ children }: { children: React.ReactNode }) {
return (
<motion.div
initial={{ opacity: 0, y: 20 }}
animate={{ opacity: 1, y: 0 }}
transition={{ duration: 0.4, ease: 'easeOut' }}
className="p-6 bg-white rounded-lg shadow"
>
{children}
</motion.div>
)
}
```
---
### Pattern 2: Staggered List Animation
```typescript
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 }
}
export function List({ items }: { items: string[] }) {
return (
<motion.ul
variants={container}
initial="hidden"
animate="show"
>
{items.map((text, i) => (
<motion.li key={i} variants={item}>
{text}
</motion.li>
))}
</motion.ul>
)
}
```
---
### Pattern 3: Button Hover Animation
```typescript
import { motion } from 'framer-motion'
export function AnimatedButton({ children, onClick }: {
children: React.ReactNode
onClick: () => void
}) {
return (
<motion.button
onClick={onClick}
whileHover={{ scale: 1.05 }}
whileTap={{ scale: 0.95 }}
transition={{ type: 'spring', stiffness: 400, damping: 17 }}
className="px-6 py-3 bg-blue-600 text-white rounded-lg"
>
{children}
</motion.button>
)
}
```
---
### Pattern 4: Modal / Dialog Animation
```typescript
import { motion, AnimatePresence } from 'framer-motion'
export function Modal({ isOpen, onClose, children }: {
isOpen: boolean
onClose: () => void
children: React.ReactNode
}) {
return (
<AnimatePresence>
{isOpen && (
<>
{/* Backdrop */}
<motion.div
initial={{ opacity: 0 }}
animate={{ opacity: 1 }}
exit={{ opacity: 0 }}
onClick={onClose}
className="fixed inset-0 bg-black/50 z-40"
/>
{/* Modal */}
<motion.div
initial={{ opacity: 0, scale: 0.9, y: 20 }}
animate={{ opacity: 1, scale: 1, y: 0 }}
exit={{ opacity: 0, scale: 0.9, y: 20 }}
transition={{ type: 'spring', damping: 25, stiffness: 300 }}
className="fixed inset-0 flex items-center justify-center z-50 p-4"
>
<div className="bg-white rounded-lg p-6 max-w-md w-full">
{children}
</div>
</motion.div>
</>
)}
</AnimatePresence>
)
}
```
---
### Pattern 5: Page Transition
```typescript
'use client'
import { motion } from 'framer-motion'
import { usePathname } from 'next/navigation'
export function PageTransition({ children }: { children: React.ReactNode }) {
const pathname = usePathname()
return (
<motion.div
key={pathname}
initial={{ opacity: 0, x: 20 }}
animate={{ opacity: 1, x: 0 }}
exit={{ opacity: 0, x: -20 }}
transition={{ duration: 0.3 }}
>
{children}
</motion.div>
)
}
// Usage in layout
export default function Layout({ children }) {
return (
<PageTransition>
{children}
</PageTransition>
)
}
```
---
## Scroll Animations
### Scroll-Triggered Animation
```typescript
import { motion, useScroll, useTransform } from 'framer-motion'
import { useRef } from 'react'
export function ScrollReveal({ children }: { children: React.ReactNode }) {
const ref = useRef(null)
const { scrollYProgress } = useScroll({
target: ref,
offset: ['start end', 'end start']
})
const opacity = useTransform(scrollYProgress, [0, 0.3, 0.7, 1], [0, 1, 1, 0])
const y = useTransform(scrollYProgress, [0, 0.3, 0.7, 1], [100, 0, 0, -100])
return (
<motion.div
ref={ref}
style={{ opacity, y }}
>
{children}
</motion.div>
)
}
```
### Parallax Effect
```typescript
import { motion, useScroll, useTransform } from 'framer-motion'
export function ParallaxSection() {
const { scrollY } = useScroll()
const y = useTransform(scrollY, [0, 500], [0, 150])
return (
<div className="relative h-screen overflow-hidden">
<motion.div
style={{ y }}
className="absolute inset-0"
>
<img src="/background.jpg" alt="" className="w-full h-full object-cover" />
</motion.div>
<div className="relative z-10 flex items-center justify-center h-full">
<h1 className="text-6xl font-bold text-white">
Parallax Effect
</h1>
</div>
</div>
)
}
```
### Scroll Progress Indicator
```typescript
import { motion, useScroll } from 'framer-motion'
export function ScrollProgress() {
const { scrollYProgress } = useScroll()
return (
<motion.div
style={{ scaleX: scrollYProgress }}
className="fixed top-0 left-0 right-0 h-1 bg-blue-600 origin-left z-50"
/>
)
}
```
---
## Loading Animations
### Spinner
```typescript
import { motion } from 'framer-motion'
export function Spinner() {
return (
<motion.div
animate={{ rotate: 360 }}
transition={{
duration: 1,
repeat: Infinity,
ease: 'linear'
}}
className="w-8 h-8 border-4 border-blue-600 border-t-transparent rounded-full"
/>
)
}
```
### Skeleton Loader
```typescript
import { motion } from 'framer-motion'
export function SkeletonLoader() {
return (
<motion.div
animate={{ opacity: [0.5, 1, 0.5] }}
transition={{
duration: 1.5,
repeat: Infinity,
ease: 'easeInOut'
}}
className="bg-gray-200 rounded h-4 w-full"
/>
)
}
```
### Pulsing Dots
```typescript
import { motion } from 'framer-motion'
const dotVariants = {
start: { scale: 0.8, opacity: 0.5 },
end: { scale: 1.2, opacity: 1 }
}
export function PulsingDots() {
return (
<div className="flex gap-2">
{[0, 1, 2].map((i) => (
<motion.div
key={i}
variants={dotVariants}
animate="end"
initial="start"
transition={{
duration: 0.6,
repeat: Infinity,
repeatType: 'reverse',
delay: i * 0.2
}}
className="w-3 h-3 bg-blue-600 rounded-full"
/>
))}
</div>
)
}
```
---
## CSS Animations
### Keyframe Animations
```css
/* Fade in animation */
@keyframes fadeIn {
from {
opacity: 0;
transform: translateY(20px);
}
to {
opacity: 1;
transform: translateY(0);
}
}
.fade-in {
animation: fadeIn 0.5s ease-out;
}
/* Slide in from right */
@keyframes slideInRight {
from {
transform: translateX(100%);
}
to {
transform: translateX(0);
}
}
.slide-in-right {
animation: slideInRight 0.3s ease-out;
}
/* Bounce */
@keyframes bounce {
0%,
100% {
transform: translateY(0);
}
50% {
transform: translateY(-10px);
}
}
.bounce {
animation: bounce 0.5s ease-in-out infinite;
}
```
### Tailwind Animations
```typescript
// tailwind.config.js
module.exports = {
theme: {
extend: {
keyframes: {
'fade-in': {
'0%': { opacity: '0', transform: 'translateY(10px)' },
'100%': { opacity: '1', transform: 'translateY(0)' }
},
'slide-in': {
'0%': { transform: 'translateX(-100%)' },
'100%': { transform: 'translateX(0)' }
}
},
animation: {
'fade-in': 'fade-in 0.5s ease-out',
'slide-in': 'slide-in 0.3s ease-out'
}
}
}
}
```
**Usage:**
```typescript
<div className="animate-fade-in">Fades in</div>
<div className="animate-slide-in">Slides in</div>
```
---
## Micro-Interactions
### Success Checkmark Animation
```typescript
import { motion } from 'framer-motion'
export function SuccessCheckmark() {
return (
<motion.svg
width="48"
height="48"
viewBox="0 0 48 48"
initial={{ scale: 0 }}
animate={{ scale: 1 }}
transition={{ type: 'spring', stiffness: 300, damping: 20 }}
>
<motion.circle
cx="24"
cy="24"
r="22"
fill="none"
stroke="#10B981"
strokeWidth="4"
initial={{ pathLength: 0 }}
animate={{ pathLength: 1 }}
transition={{ duration: 0.5 }}
/>
<motion.path
d="M12 24 L20 32 L36 16"
fill="none"
stroke="#10B981"
strokeWidth="4"
strokeLinecap="round"
strokeLinejoin="round"
initial={{ pathLength: 0 }}
animate={{ pathLength: 1 }}
transition={{ duration: 0.3, delay: 0.3 }}
/>
</motion.svg>
)
}
```
### Notification Badge
```typescript
import { motion } from 'framer-motion'
export function NotificationBadge({ count }: { count: number }) {
return (
<div className="relative">
<button className="p-2">
<BellIcon />
</button>
{count > 0 && (
<motion.div
initial={{ scale: 0 }}
animate={{ scale: 1 }}
transition={{ type: 'spring', stiffness: 500, damping: 15 }}
className="absolute -top-1 -right-1 bg-red-600 text-white text-xs rounded-full w-5 h-5 flex items-center justify-center"
>
{count}
</motion.div>
)}
</div>
)
}
```
---
## Animation Best Practices
### 1. Performance
```typescript
// ✅ Good: Animate transform and opacity (GPU accelerated)
<motion.div
animate={{ x: 100, opacity: 0.5 }}
/>
// ❌ Bad: Animate width, height (triggers layout)
<motion.div
animate={{ width: '100%', height: '200px' }}
/>
```
### 2. Duration
```typescript
// Too fast: < 100ms (feels abrupt)
// Too slow: > 500ms (feels sluggish)
// ✅ Sweet spot: 200-400ms for most UI animations
<motion.div
animate={{ opacity: 1 }}
transition={{ duration: 0.3 }}
/>
```
### 3. Easing
```typescript
// Natural motion: easeOut (starts fast, ends slow)
<motion.div
animate={{ y: 0 }}
transition={{ ease: 'easeOut' }}
/>
// Bouncy: spring
<motion.button
whileTap={{ scale: 0.95 }}
transition={{ type: 'spring', stiffness: 400 }}
/>
```
### 4. Reduce Motion (Accessibility)
```typescript
import { useReducedMotion } from 'framer-motion'
export function AccessibleAnimation({ children }: { children: React.ReactNode }) {
const shouldReduceMotion = useReducedMotion()
return (
<motion.div
initial={{ opacity: 0, y: shouldReduceMotion ? 0 : 20 }}
animate={{ opacity: 1, y: 0 }}
transition={{
duration: shouldReduceMotion ? 0 : 0.4
}}
>
{children}
</motion.div>
)
}
```
---
## Complex Animations
### Drag and Drop
```typescript
import { motion } from 'framer-motion'
import { useState } from 'react'
export function Draggable() {
const [position, setPosition] = useState({ x: 0, y: 0 })
return (
<motion.div
drag
dragConstraints={{ left: 0, right: 300, top: 0, bottom: 300 }}
dragElastic={0.1}
onDragEnd={(e, info) => {
setPosition({ x: info.point.x, y: info.point.y })
}}
className="w-24 h-24 bg-blue-600 rounded-lg cursor-grab active:cursor-grabbing"
/>
)
}
```
### Animated Number Counter
```typescript
import { motion, useSpring, useTransform } from 'framer-motion'
import { useEffect } from 'react'
export function AnimatedNumber({ value }: { value: number }) {
const spring = useSpring(0, { stiffness: 100, damping: 30 })
const display = useTransform(spring, (current) =>
Math.round(current).toLocaleString()
)
useEffect(() => {
spring.set(value)
}, [spring, value])
return <motion.span>{display}</motion.span>
}
// Usage
<AnimatedNumber value={1250} />
```
---
## When to Use Me
**Perfect for:**
- Creating polished UI animations
- Building interactive components
- Adding scroll effects
- Designing loading states
- Improving user feedback
**I'll help you:**
- Choose the right animation type
- Implement smooth transitions
- Optimize animation performance
- Ensure accessibility
- Create delightful micro-interactions
## What I'll Create
```
✨ Page Transitions
🎯 Micro-Interactions
📜 Scroll Animations
⏳ Loading States
🎨 Hover Effects
🎪 Complex Animations
```
Let's make your interfaces feel alive!
© daffy0208 · MIT · daffy0208/ai-dev-standards