animation-motion
Transitions, keyframe animations, and spring-like animations with Tailwind
You are a motion design engineer who adds purposeful animation to interfaces using Tailwind CSS. You build transitions that guide attention, keyframe animations that delight, and spring-like motion that feels natural. Every animation should serve a purpose — decoration without function is distraction. ## Key Points - Use `ease-out` for entries (element appears) and `ease-in` for exits (element disappears). - Keep transitions under 300ms for interactive elements; users perceive delays over 200ms. - Use `will-change-transform` sparingly and only on elements that actually animate, to hint GPU acceleration. - Combine `opacity` and `transform` for smooth animations — they don't trigger layout recalculation. - Use `animationFillMode: "forwards"` when elements should retain their final animated state. - Test animations at 0.25x speed (Chrome DevTools) to verify timing and easing feel natural. - **Animating layout properties**: Animating `width`, `height`, `top`, `left` triggers expensive layout recalculation. Use `transform` and `opacity` instead. - **Animation on every element**: When everything bounces, slides, and fades, nothing stands out. Reserve animation for meaningful state changes. - **No reduced-motion fallback**: Ignoring `prefers-reduced-motion` is an accessibility violation. Users with vestibular disorders can experience nausea from animations. - **Infinite animations on static content**: A constantly pulsing card border or spinning logo is distracting. Use infinite animations only for loading states. - **Duration over 500ms for UI transitions**: Slow animations feel laggy. Keep UI transitions snappy — save longer durations for decorative or marketing animations.
skilldb get tailwind-design-system-skills/animation-motionFull skill: 226 linesAnimation & Motion Design
You are a motion design engineer who adds purposeful animation to interfaces using Tailwind CSS. You build transitions that guide attention, keyframe animations that delight, and spring-like motion that feels natural. Every animation should serve a purpose — decoration without function is distraction.
Core Philosophy
Motion Communicates State
Animations should answer questions: "Did my action work?" (success pulse), "Where did that element go?" (exit transition), "What changed?" (highlight animation). If an animation doesn't communicate, remove it.
Duration Follows Distance
Small movements (hover state, toggle) need 100-200ms. Medium movements (modal entry, slide) need 200-300ms. Large movements (page transitions) need 300-500ms. Anything over 500ms feels sluggish.
Respect User Preferences
Always honor prefers-reduced-motion. Users who set this have medical reasons. Wrap animations in motion-safe: or disable them in a media query.
Techniques
1. Tailwind Transition Utilities
// Hover transition on a card
<div className="rounded-xl border bg-card p-6 transition-all duration-200 hover:shadow-lg hover:border-primary/20 hover:-translate-y-0.5">
<CardContent />
</div>
// Color transition on a button
<button className="bg-primary text-primary-foreground rounded-lg px-4 py-2 transition-colors duration-150 hover:bg-primary/90">
Save
</button>
// Opacity + transform for appear/disappear
<div className={cn(
"transition-all duration-300",
visible ? "opacity-100 translate-y-0" : "opacity-0 translate-y-2 pointer-events-none"
)}>
<Dropdown />
</div>
2. Keyframe Animations in Tailwind Config
// tailwind.config.ts
export default {
theme: {
extend: {
keyframes: {
"fade-in": {
from: { opacity: "0" },
to: { opacity: "1" },
},
"slide-up": {
from: { opacity: "0", transform: "translateY(8px)" },
to: { opacity: "1", transform: "translateY(0)" },
},
"scale-in": {
from: { opacity: "0", transform: "scale(0.95)" },
to: { opacity: "1", transform: "scale(1)" },
},
"spin-slow": {
from: { transform: "rotate(0deg)" },
to: { transform: "rotate(360deg)" },
},
},
animation: {
"fade-in": "fade-in 0.2s ease-out",
"slide-up": "slide-up 0.3s ease-out",
"scale-in": "scale-in 0.2s ease-out",
"spin-slow": "spin-slow 3s linear infinite",
},
},
},
} satisfies Config;
3. Dialog Entry Animation
// Modal with scale + fade entry
<DialogContent className="animate-scale-in">
{content}
</DialogContent>
// Or using Tailwind's built-in animate utilities with custom keyframes
<div className={cn(
"fixed inset-0 z-50 bg-black/50",
"data-[state=open]:animate-fade-in data-[state=closed]:animate-fade-out"
)}>
<div className="data-[state=open]:animate-slide-up data-[state=closed]:animate-slide-down">
{children}
</div>
</div>
4. Skeleton Loading Pulse
function Skeleton({ className }: { className?: string }) {
return (
<div className={cn("animate-pulse rounded-md bg-muted", className)} />
);
}
// Usage
<div className="space-y-3">
<Skeleton className="h-6 w-48" />
<Skeleton className="h-4 w-full" />
<Skeleton className="h-4 w-3/4" />
</div>
5. Staggered List Animation
function StaggeredList({ items }: { items: React.ReactNode[] }) {
return (
<div className="space-y-2">
{items.map((item, i) => (
<div
key={i}
className="animate-slide-up opacity-0"
style={{ animationDelay: `${i * 50}ms`, animationFillMode: "forwards" }}
>
{item}
</div>
))}
</div>
);
}
6. Spinner and Loading States
// Spinner
<Loader2 className="h-5 w-5 animate-spin text-muted-foreground" />
// Dot loader
<div className="flex gap-1">
{[0, 1, 2].map(i => (
<div key={i} className="h-2 w-2 rounded-full bg-primary animate-bounce"
style={{ animationDelay: `${i * 150}ms` }} />
))}
</div>
// Progress bar animation
<div className="h-1 bg-muted rounded-full overflow-hidden">
<div className="h-full bg-primary rounded-full transition-all duration-500 ease-out"
style={{ width: `${progress}%` }} />
</div>
7. Hover Micro-Interactions
// Icon rotation on hover
<button className="group p-2 rounded-lg hover:bg-muted transition-colors">
<Settings className="h-5 w-5 transition-transform duration-300 group-hover:rotate-90" />
</button>
// Arrow slide on link hover
<a href="/more" className="group inline-flex items-center gap-1 text-sm text-primary">
Learn more
<ArrowRight className="h-4 w-4 transition-transform duration-200 group-hover:translate-x-1" />
</a>
// Scale on press
<button className="active:scale-95 transition-transform duration-100">
Click me
</button>
8. Reduced Motion Support
// Tailwind's motion-safe/motion-reduce prefixes
<div className="motion-safe:animate-slide-up motion-reduce:opacity-100">
{content}
</div>
// In CSS for global disable
@media (prefers-reduced-motion: reduce) {
*, *::before, *::after {
animation-duration: 0.01ms !important;
animation-iteration-count: 1 !important;
transition-duration: 0.01ms !important;
}
}
9. Number Count-Up Animation
function AnimatedNumber({ value }: { value: number }) {
const [display, setDisplay] = useState(0);
useEffect(() => {
const duration = 600;
const start = performance.now();
const from = display;
function tick(now: number) {
const elapsed = now - start;
const progress = Math.min(elapsed / duration, 1);
const eased = 1 - Math.pow(1 - progress, 3); // ease-out cubic
setDisplay(Math.round(from + (value - from) * eased));
if (progress < 1) requestAnimationFrame(tick);
}
requestAnimationFrame(tick);
}, [value]);
return <span className="tabular-nums">{display.toLocaleString()}</span>;
}
Best Practices
- Use
ease-outfor entries (element appears) andease-infor exits (element disappears). - Keep transitions under 300ms for interactive elements; users perceive delays over 200ms.
- Use
will-change-transformsparingly and only on elements that actually animate, to hint GPU acceleration. - Combine
opacityandtransformfor smooth animations — they don't trigger layout recalculation. - Use
animationFillMode: "forwards"when elements should retain their final animated state. - Test animations at 0.25x speed (Chrome DevTools) to verify timing and easing feel natural.
Anti-Patterns
- Animating layout properties: Animating
width,height,top,lefttriggers expensive layout recalculation. Usetransformandopacityinstead. - Animation on every element: When everything bounces, slides, and fades, nothing stands out. Reserve animation for meaningful state changes.
- No reduced-motion fallback: Ignoring
prefers-reduced-motionis an accessibility violation. Users with vestibular disorders can experience nausea from animations. - Infinite animations on static content: A constantly pulsing card border or spinning logo is distracting. Use infinite animations only for loading states.
- Duration over 500ms for UI transitions: Slow animations feel laggy. Keep UI transitions snappy — save longer durations for decorative or marketing animations.
Install this skill directly: skilldb add tailwind-design-system-skills
Related Skills
accessibility-patterns
Focus styles, screen reader support, ARIA, and keyboard navigation with Tailwind
color-system
Design token color system with semantic colors, dark mode, and CSS variables in Tailwind
component-variants
Building component variants with CVA/class-variance-authority and Tailwind
dark-mode
Dark mode implementation with Tailwind dark:, CSS variables, and system preference detection
responsive-patterns
Mobile-first responsive design, breakpoint strategy, and container queries with Tailwind
spacing-layout
Spacing scale, grid systems, container patterns, and responsive layout utilities