---
name: bellog-animations
description: Provides Framer Motion animation patterns and best practices specific to the Bellog blog project. Triggers when implementing animated components or interactions.
---
# Bellog Animation Patterns
This skill provides the animation patterns and best practices used throughout the Bellog blog project.
## Core Animation Principles
1. **Organic movement** - Use easing curves, never linear
2. **Stagger for rhythm** - Create visual flow with staggered animations
3. **Living elements** - Ambient animations that breathe life
4. **Consistent timing** - Follow project timing standards
## Animation Timing Standards
```typescript
// Interaction timings
const INTERACTION_FAST = 0.2; // Button press, hover start
const INTERACTION_NORMAL = 0.3; // Standard hover effects
const INTERACTION_SLOW = 0.5; // Modal open, drawer slide
// Transition timings
const TRANSITION_FAST = 0.3; // Quick state changes
const TRANSITION_NORMAL = 0.4; // Page transitions (standard)
const TRANSITION_SLOW = 0.6; // Heavy content transitions
// Ambient timings
const AMBIENT_SLOW = 3; // Slow blob movement
const AMBIENT_NORMAL = 4; // Standard blob rhythm
const AMBIENT_FAST = 5; // Faster ambient motion
```
## Pattern 1: Stagger Children
Use for lists, grids, and groups of elements.
**When to use:** Animating multiple items that should appear in sequence
**Example from Intro.tsx:**
```typescript
const container = {
hidden: { opacity: 0 },
show: {
opacity: 1,
transition: {
staggerChildren: 0.1, // 100ms delay between each child
delayChildren: 0.2 // Start after 200ms
}
}
};
const item = {
hidden: { y: 20, opacity: 0 },
show: {
y: 0,
opacity: 1,
transition: {
duration: 0.5,
ease: "easeInOut"
}
}
};
// Usage
{items.map(item => (
{item.content}
))}
```
## Pattern 2: Living Blob Animations
Use for decorative elements, background shapes, ambient animations.
**When to use:** Creating organic, perpetual movement
**Example from Intro.tsx:**
```typescript
const blobVariants = {
initial: {
scale: 1,
x: 0,
y: 0
},
hover: {
scale: [1, 1.2, 0.9, 1.1, 1], // Keyframes
x: [0, 20, -10, 5, 0],
y: [0, -15, 10, -5, 0],
transition: {
duration: 4,
repeat: Infinity,
repeatType: "mirror",
ease: "easeInOut"
}
}
};
// Multiple blobs with different rhythms
const blob1 = { duration: 3, ... };
const blob2 = { duration: 4.5, ... };
const blob3 = { duration: 5.2, ... };
```
**Best practices:**
- Layer multiple blobs with different durations for organic feel
- Use `repeatType: "mirror"` for smooth loops
- Keep movements subtle (scale: 0.9-1.2 range)
- Use blur and opacity to create depth
## Pattern 3: Page Transitions
Use for route changes, content swapping.
**When to use:** Navigating between pages or major content changes
**Example from template.tsx:**
```typescript
{children}
```
**Variations:**
```typescript
// Fade only
initial={{ opacity: 0 }}
animate={{ opacity: 1 }}
// Slide from right
initial={{ opacity: 0, x: 20 }}
animate={{ opacity: 1, x: 0 }}
// Scale + fade
initial={{ opacity: 0, scale: 0.95 }}
animate={{ opacity: 1, scale: 1 }}
```
## Pattern 4: Hover Interactions
Use for buttons, cards, clickable elements.
**When to use:** Adding interactivity to user-actionable elements
```typescript
const cardVariants = {
initial: { scale: 1 },
hover: {
scale: 1.02,
transition: {
duration: 0.3,
ease: "easeInOut"
}
},
tap: {
scale: 0.98
}
};
```
**Common hover patterns:**
- **Cards:** scale(1.02) + shadow increase
- **Buttons:** scale(1.05) + slight lift
- **Icons:** rotate or scale
- **Links:** underline expand
## Pattern 5: Scroll-Based Animations
Use for parallax, fade-ins, progress indicators.
**When to use:** Animations triggered by scroll position
```typescript
import { useScroll, useTransform } from "framer-motion";
const { scrollYProgress } = useScroll();
const opacity = useTransform(scrollYProgress, [0, 0.5], [0, 1]);
const y = useTransform(scrollYProgress, [0, 0.5], [50, 0]);
{/* Content */}
```
**Example: Progress bar**
```typescript
const { scrollYProgress } = useScroll();
```
## Pattern 6: Enter/Exit Animations
Use with AnimatePresence for conditional rendering.
**When to use:** Elements that appear and disappear
```typescript
import { AnimatePresence } from "framer-motion";
const variants = {
hidden: { opacity: 0, y: -10 },
visible: { opacity: 1, y: 0 },
exit: { opacity: 0, y: -10 }
};
{isVisible && (
{content}
)}
```
## Easing Functions
**Use these, not "linear":**
```typescript
// Bellog standard
ease: "easeInOut" // Default for most animations
// Other options
ease: "easeOut" // For entrances
ease: "easeIn" // For exits
ease: [0.43, 0.13, 0.23, 0.96] // Custom cubic-bezier
```
## Animation Checklist
Before finalizing animations:
- [ ] Timing follows project standards (0.2-0.5s for interactions)
- [ ] Uses `variants` pattern (not inline animation props)
- [ ] Easing is `easeInOut` or appropriate alternative
- [ ] No linear easing
- [ ] Stagger delay appropriate for number of items
- [ ] Exit animations defined if using AnimatePresence
- [ ] Performance: No layout thrashing (avoid animating width/height)
- [ ] Accessibility: Respects `prefers-reduced-motion` if critical
## Performance Tips
1. **Transform over top/left:**
```typescript
// ✅ Good (GPU accelerated)
{ x: 100 }
{ translateX: "100px" }
{ scale: 1.1 }
// ❌ Bad (layout recalc)
{ left: 100 }
{ width: "100%" }
```
2. **Will-change hint:**
```typescript
style={{ willChange: "transform" }}
```
3. **Layout animations (use sparingly):**
```typescript
```
## Reduced Motion
Respect user preferences:
```typescript
import { useReducedMotion } from "framer-motion";
const shouldReduceMotion = useReducedMotion();
const variants = {
hidden: { opacity: 0, y: shouldReduceMotion ? 0 : 20 },
visible: { opacity: 1, y: 0 }
};
```
## Common Mistakes to Avoid
❌ **Don't:** Inline animation props
```typescript
```
✅ **Do:** Use variants
```typescript
const variants = { hidden: { opacity: 0 }, visible: { opacity: 1 } };
```
---
❌ **Don't:** Linear easing
```typescript
transition={{ duration: 0.3, ease: "linear" }}
```
✅ **Do:** Use curves
```typescript
transition={{ duration: 0.3, ease: "easeInOut" }}
```
---
❌ **Don't:** Animate width/height directly
```typescript
animate={{ width: "100%" }}
```
✅ **Do:** Use scale or layout
```typescript
animate={{ scaleX: 1 }}
// or
```
## Quick Reference
```typescript
// Standard fade + slide in
{ initial: { opacity: 0, y: 20 }, animate: { opacity: 1, y: 0 } }
// Standard hover
{ whileHover: { scale: 1.02 }, transition: { duration: 0.3 } }
// Standard stagger
{
variants: {
container: { transition: { staggerChildren: 0.1 } },
item: { hidden: { y: 20, opacity: 0 }, show: { y: 0, opacity: 1 } }
}
}
// Standard exit
{ exit: { opacity: 0, y: -10 }, transition: { duration: 0.2 } }
```
Remember: Animations should enhance the experience, not distract from it. When in doubt, keep it subtle and fast.