Tailwind Animations
Animation utilities, transitions, custom keyframes, and motion patterns with Tailwind CSS
You are an expert in implementing animations and transitions with Tailwind CSS. ## Key Points - **Keep animations subtle and fast.** 150-300ms is the sweet spot for UI transitions. Anything over 500ms feels sluggish. - **Use `ease-out` for entrances, `ease-in` for exits.** Elements entering view should decelerate; elements leaving should accelerate. - **Always support `prefers-reduced-motion`.** Use the `motion-reduce:` variant to disable or simplify animations for users who need it. - **Animate transforms and opacity.** These are GPU-accelerated and do not trigger layout recalculation. Avoid animating `width`, `height`, `top`, or `left`. - **Use `will-change` sparingly.** Adding `will-change-transform` can help with jank, but overuse wastes GPU memory. - **Prefer `transition` for state changes and `animate` for looping/entrance animations.** - **Animating layout properties.** Transitioning `width`, `height`, `padding`, or `margin` causes expensive reflows. Use `transform: scale()` or `max-height` workarounds instead. - **Forgetting `transform` base class.** In Tailwind v3, `hover:-translate-y-1` requires the `transform` class on the element (in v3.0-3.3). In later versions and v4, this is automatic. - **Not testing on low-end devices.** Animations that are smooth on a fast laptop may stutter on budget phones. Always test on target devices. ## Quick Example ```html <div class="animate-fade-in-up">Fades in from below</div> <div class="animate-scale-in">Scales up into view</div> ``` ```html <!-- Opt-in to view transitions --> <meta name="view-transition" content="same-origin" /> ```
skilldb get tailwind-skills/Tailwind AnimationsFull skill: 284 linesAnimation Utilities and Custom Animations — Tailwind CSS
You are an expert in implementing animations and transitions with Tailwind CSS.
Overview
Tailwind provides built-in transition utilities, transform utilities, and a set of keyframe animations out of the box. For more complex animations, you define custom keyframes in tailwind.config.js and reference them as animation utilities. Combined with state variants like hover:, group-hover:, and JavaScript-driven class toggling, Tailwind handles everything from subtle micro-interactions to full entrance animations.
Core Philosophy
Animation in Tailwind should be invisible when done right. The goal is not to showcase motion for its own sake but to make interfaces feel responsive and alive. Every transition should serve a communicative purpose — confirming a user action, revealing new content, or establishing spatial relationships between elements. When a user clicks a button and it subtly depresses, or when a dropdown menu fades in rather than appearing from nowhere, the interface communicates that it is listening and responding.
The utility-first approach to animation means reaching for Tailwind's built-in transition and animation classes before writing custom CSS. Most UI motion needs are covered by combining transition-*, duration-*, and ease-* utilities with state variants like hover: and focus:. Custom keyframe animations belong in the config file, not scattered across component stylesheets, so the team shares a consistent motion vocabulary. This keeps animations discoverable and prevents the codebase from accumulating dozens of one-off CSS animations that subtly differ.
Performance is a non-negotiable constraint. Animating only transform and opacity ensures GPU acceleration and avoids layout thrashing. Accessibility is equally non-negotiable — every animation must degrade gracefully via motion-reduce: variants, because motion that delights one user can disorient another.
Core Concepts
Built-in Transition Utilities
Transitions control how properties animate when they change:
<!-- Transition all properties -->
<button class="bg-blue-600 transition-all duration-200 ease-in-out hover:bg-blue-700 hover:shadow-lg">
Hover me
</button>
<!-- Transition specific properties -->
<div class="transition-colors duration-150">Color transitions only</div>
<div class="transition-transform duration-300">Transform transitions only</div>
<div class="transition-opacity duration-500">Opacity transitions only</div>
| Utility | Properties |
|---|---|
transition-none | None |
transition-all | All properties |
transition-colors | color, background-color, border-color, etc. |
transition-opacity | opacity |
transition-shadow | box-shadow |
transition-transform | transform |
transition | Default set (colors, opacity, shadow, transform) |
Duration and Easing
<div class="transition duration-75">75ms</div>
<div class="transition duration-150">150ms</div>
<div class="transition duration-300">300ms (good default)</div>
<div class="transition duration-500">500ms</div>
<div class="transition duration-1000">1000ms</div>
<div class="transition ease-linear">Linear</div>
<div class="transition ease-in">Ease in (slow start)</div>
<div class="transition ease-out">Ease out (slow end)</div>
<div class="transition ease-in-out">Ease in-out</div>
Built-in Keyframe Animations
Tailwind ships with four keyframe animations:
<!-- Spinner -->
<svg class="animate-spin h-5 w-5 text-blue-600" viewBox="0 0 24 24">...</svg>
<!-- Pulsing skeleton loader -->
<div class="animate-pulse rounded-md bg-gray-200 h-4 w-3/4"></div>
<!-- Notification ping -->
<span class="relative flex h-3 w-3">
<span class="animate-ping absolute inline-flex h-full w-full rounded-full bg-sky-400 opacity-75"></span>
<span class="relative inline-flex h-3 w-3 rounded-full bg-sky-500"></span>
</span>
<!-- Bounce indicator -->
<svg class="animate-bounce h-6 w-6 text-gray-400" viewBox="0 0 24 24">...</svg>
Implementation Patterns
Custom Keyframe Animations
// tailwind.config.js
module.exports = {
theme: {
extend: {
keyframes: {
'fade-in': {
'0%': { opacity: '0' },
'100%': { opacity: '1' },
},
'fade-in-up': {
'0%': { opacity: '0', transform: 'translateY(16px)' },
'100%': { opacity: '1', transform: 'translateY(0)' },
},
'fade-in-down': {
'0%': { opacity: '0', transform: 'translateY(-16px)' },
'100%': { opacity: '1', transform: 'translateY(0)' },
},
'slide-in-right': {
'0%': { transform: 'translateX(100%)' },
'100%': { transform: 'translateX(0)' },
},
'slide-in-left': {
'0%': { transform: 'translateX(-100%)' },
'100%': { transform: 'translateX(0)' },
},
'scale-in': {
'0%': { opacity: '0', transform: 'scale(0.95)' },
'100%': { opacity: '1', transform: 'scale(1)' },
},
'accordion-down': {
'0%': { height: '0', opacity: '0' },
'100%': { height: 'var(--radix-accordion-content-height)', opacity: '1' },
},
'accordion-up': {
'0%': { height: 'var(--radix-accordion-content-height)', opacity: '1' },
'100%': { height: '0', opacity: '0' },
},
},
animation: {
'fade-in': 'fade-in 0.3s ease-out',
'fade-in-up': 'fade-in-up 0.4s ease-out',
'fade-in-down': 'fade-in-down 0.4s ease-out',
'slide-in-right': 'slide-in-right 0.3s ease-out',
'slide-in-left': 'slide-in-left 0.3s ease-out',
'scale-in': 'scale-in 0.2s ease-out',
'accordion-down': 'accordion-down 0.2s ease-out',
'accordion-up': 'accordion-up 0.2s ease-out',
},
},
},
}
Usage:
<div class="animate-fade-in-up">Fades in from below</div>
<div class="animate-scale-in">Scales up into view</div>
Hover Micro-Interactions
<!-- Lift on hover -->
<div class="rounded-lg bg-white p-6 shadow-md transition-all duration-200 hover:-translate-y-1 hover:shadow-xl">
Card that lifts on hover
</div>
<!-- Scale button -->
<button class="transform rounded-md bg-indigo-600 px-4 py-2 text-white transition-transform duration-150 hover:scale-105 active:scale-95">
Press me
</button>
<!-- Icon rotation -->
<button class="group flex items-center gap-2">
<span>Settings</span>
<svg class="h-4 w-4 transition-transform duration-300 group-hover:rotate-90">...</svg>
</button>
<!-- Underline slide-in -->
<a href="#" class="relative text-gray-700 hover:text-gray-900">
<span>Link text</span>
<span class="absolute bottom-0 left-0 h-0.5 w-0 bg-indigo-600 transition-all duration-300 group-hover:w-full"></span>
</a>
Skeleton Loading Screen
<div class="animate-pulse space-y-4">
<!-- Avatar + name -->
<div class="flex items-center gap-4">
<div class="h-12 w-12 rounded-full bg-gray-200"></div>
<div class="space-y-2">
<div class="h-4 w-32 rounded bg-gray-200"></div>
<div class="h-3 w-24 rounded bg-gray-200"></div>
</div>
</div>
<!-- Content lines -->
<div class="space-y-2">
<div class="h-4 w-full rounded bg-gray-200"></div>
<div class="h-4 w-5/6 rounded bg-gray-200"></div>
<div class="h-4 w-4/6 rounded bg-gray-200"></div>
</div>
</div>
Staggered Entrance Animations
Use inline animation-delay with a custom utility or arbitrary values:
<div class="space-y-4">
<div class="animate-fade-in-up opacity-0" style="animation-delay: 0ms; animation-fill-mode: forwards">Item 1</div>
<div class="animate-fade-in-up opacity-0" style="animation-delay: 100ms; animation-fill-mode: forwards">Item 2</div>
<div class="animate-fade-in-up opacity-0" style="animation-delay: 200ms; animation-fill-mode: forwards">Item 3</div>
<div class="animate-fade-in-up opacity-0" style="animation-delay: 300ms; animation-fill-mode: forwards">Item 4</div>
</div>
Or add delay utilities in config:
// tailwind.config.js
extend: {
animationDelay: {
'100': '100ms',
'200': '200ms',
'300': '300ms',
'500': '500ms',
},
}
Page Transitions with View Transitions API
<!-- Opt-in to view transitions -->
<meta name="view-transition" content="same-origin" />
/* Customize with Tailwind-compatible CSS */
::view-transition-old(root) {
animation: fade-out 0.2s ease-in;
}
::view-transition-new(root) {
animation: fade-in 0.3s ease-out;
}
Reduced Motion Accessibility
<!-- Respect prefers-reduced-motion -->
<div class="animate-fade-in-up motion-reduce:animate-none motion-reduce:opacity-100">
Animated unless user prefers reduced motion
</div>
<!-- Alternative: only animate if user is OK with motion -->
<div class="opacity-0 motion-safe:animate-fade-in-up motion-safe:opacity-100">
Only animates if motion is acceptable
</div>
Best Practices
- Keep animations subtle and fast. 150-300ms is the sweet spot for UI transitions. Anything over 500ms feels sluggish.
- Use
ease-outfor entrances,ease-infor exits. Elements entering view should decelerate; elements leaving should accelerate. - Always support
prefers-reduced-motion. Use themotion-reduce:variant to disable or simplify animations for users who need it. - Animate transforms and opacity. These are GPU-accelerated and do not trigger layout recalculation. Avoid animating
width,height,top, orleft. - Use
will-changesparingly. Addingwill-change-transformcan help with jank, but overuse wastes GPU memory. - Prefer
transitionfor state changes andanimatefor looping/entrance animations.
Common Pitfalls
- Animating layout properties. Transitioning
width,height,padding, ormargincauses expensive reflows. Usetransform: scale()ormax-heightworkarounds instead. - Missing
animation-fill-mode: forwards. Without it, elements revert to their pre-animation state after the animation completes. Setopacity-0as initial and usefill-mode: forwardsto keep the final frame. - Forgetting
transformbase class. In Tailwind v3,hover:-translate-y-1requires thetransformclass on the element (in v3.0-3.3). In later versions and v4, this is automatic. - Stacking too many transitions. Applying
transition-allon complex elements with many changing properties can cause performance issues. Be specific withtransition-colorsortransition-transform. - Not testing on low-end devices. Animations that are smooth on a fast laptop may stutter on budget phones. Always test on target devices.
Anti-Patterns
-
Animation soup. Applying entrance animations, hover effects, and infinite loops to multiple elements on the same page creates a chaotic, distracting experience. Limit simultaneous animations and let the primary content hold the user's attention.
-
CSS
transition: allon complex components. This animates every changing property including layout-triggering ones likewidthandpadding, causing jank and unintended visual artifacts. Always specify exactly which properties to transition. -
Ignoring
prefers-reduced-motionentirely. Shipping animations withoutmotion-reduce:ormotion-safe:variants excludes users with vestibular disorders. This is not an edge case — it is an accessibility failure. -
Recreating JavaScript animation libraries in Tailwind. Tailwind handles CSS transitions and keyframe animations well, but complex orchestrated sequences, spring physics, or scroll-linked animations belong in dedicated libraries like Framer Motion or GSAP. Forcing these into utility classes produces unmaintainable markup.
-
Staggered animations without
animation-fill-mode: forwards. Elements flash to their pre-animation state after the animation completes, creating a jarring flicker. Always pair staggered entrances withfill-mode: forwardsand an initial hidden state likeopacity-0.
Install this skill directly: skilldb add tailwind-skills
Related Skills
Tailwind Component Patterns
Common UI component patterns including cards, navbars, forms, modals, and badges built with Tailwind CSS
Tailwind Custom Config
Customizing tailwind.config.js to extend themes, add custom colors, fonts, spacing, and configure content paths
Tailwind Dark Mode
Dark mode strategies including class-based toggling, media queries, and CSS variable theming with Tailwind CSS
Tailwind Fundamentals
Utility-first CSS fundamentals with Tailwind including class composition, spacing, typography, and layout primitives
Tailwind Plugins
Writing custom Tailwind CSS plugins to add utilities, components, base styles, and variants
Tailwind Responsive Design
Responsive breakpoints, mobile-first design patterns, and adaptive layouts with Tailwind CSS