On this page
Transitions and Animations
Transitions and Animations
Motion and animation are powerful communication tools in UI design. Smooth transitions guide the user's eye, provide feedback for interactions, and make the interface feel alive and responsive. Tailwind provides a complete set of utilities for both CSS transitions and keyframe animations.
CSS transitions
A CSS transition animates a property from one value to another when it changes. You need three things: what to transition, how long, and with what easing.
Enabling transitions
<!-- transition: applies to common properties (colors, opacity, shadow, transform) -->
<button class="bg-indigo-600 hover:bg-indigo-700 transition">Default transition</button>
<!-- Specific property targets -->
<div class="transition-colors">Only colors transition</div>
<div class="transition-opacity">Only opacity transitions</div>
<div class="transition-shadow">Only shadow transitions</div>
<div class="transition-transform">Only transforms transition</div>
<div class="transition-all">All properties transition (use carefully)</div>
<div class="transition-none">Disable all transitions</div>Duration
<div class="transition duration-75">75ms — very fast</div>
<div class="transition duration-100">100ms — fast (good for small elements)</div>
<div class="transition duration-150">150ms — quick (default buttons)</div>
<div class="transition duration-200">200ms — standard (hover effects)</div>
<div class="transition duration-300">300ms — moderate (page sections)</div>
<div class="transition duration-500">500ms — slow (modals, drawers)</div>
<div class="transition duration-700">700ms — deliberate</div>
<div class="transition duration-1000">1000ms — dramatic</div>
<!-- Arbitrary duration -->
<div class="transition duration-[400ms]">400ms custom</div>Easing (timing functions)
<div class="transition ease-linear">Constant speed</div>
<div class="transition ease-in">Starts slow, ends fast (good for exit animations)</div>
<div class="transition ease-out">Starts fast, ends slow (good for enter animations)</div>
<div class="transition ease-in-out">Slow at both ends (good for hover effects)</div>
<!-- Arbitrary easing -->
<div class="transition ease-[cubic-bezier(0.34,1.56,0.64,1)]">Spring-like bounce</div>Delay
<div class="transition delay-75">75ms delay before transition starts</div>
<div class="transition delay-150">150ms delay</div>
<div class="transition delay-300">300ms delay</div>
<!-- Staggered list items using delay -->
<ul>
<li class="transition-all duration-300 delay-0 opacity-0 translate-y-2 show:opacity-100 show:translate-y-0">Item 1</li>
<li class="transition-all duration-300 delay-75 opacity-0 translate-y-2 show:opacity-100 show:translate-y-0">Item 2</li>
<li class="transition-all duration-300 delay-150 opacity-0 translate-y-2 show:opacity-100 show:translate-y-0">Item 3</li>
</ul>CSS transforms
Transforms (translate, scale, rotate, skew) are the backbone of performant animation because they do not trigger layout recalculation.
<!-- Scale -->
<div class="hover:scale-105 transition-transform duration-200">Grows on hover</div>
<div class="hover:scale-95 active:scale-90 transition-transform">Shrinks on press</div>
<div class="hover:scale-x-110 transition-transform">Stretches horizontally</div>
<!-- Translate (move without affecting layout) -->
<div class="hover:-translate-y-1 transition-transform duration-200">Floats up on hover</div>
<div class="hover:translate-x-1 transition-transform">Nudges right on hover</div>
<!-- Rotate -->
<svg class="hover:rotate-12 transition-transform duration-200">...</svg>
<svg class="group-hover:rotate-180 transition-transform duration-300">Chevron flips</svg>
<!-- Combining transforms -->
<div class="hover:scale-105 hover:-translate-y-2 transition-transform duration-200">
Scale AND lift
</div>Built-in animations
Tailwind provides four keyframe animations out of the box.
animate-spin
For loading indicators:
<!-- Classic spinner -->
<div
class="w-8 h-8 rounded-full border-4 border-gray-200 border-t-indigo-600 animate-spin"
role="status"
aria-label="Loading"
></div>
<!-- Inline loading state on a button -->
<button class="flex items-center gap-2 bg-indigo-600 text-white px-4 py-2 rounded-lg">
<svg class="w-4 h-4 animate-spin" fill="none" viewBox="0 0 24 24">
<circle class="opacity-25" cx="12" cy="12" r="10" stroke="currentColor" stroke-width="4"></circle>
<path class="opacity-75" fill="currentColor" d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4z"></path>
</svg>
Saving...
</button>animate-ping
For notification badges and "live" indicators:
<!-- Online indicator -->
<div class="relative inline-flex">
<div class="w-3 h-3 bg-green-500 rounded-full"></div>
<div class="absolute inset-0 w-3 h-3 bg-green-400 rounded-full animate-ping"></div>
</div>
<!-- Notification badge -->
<div class="relative inline-block">
<button class="p-2 rounded-lg bg-gray-100">
<svg class="w-6 h-6 text-gray-600" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M15 17h5l-1.405-1.405A2.032 2.032 0 0118 14.158V11a6.002 6.002 0 00-4-5.659V5a2 2 0 10-4 0v.341C7.67 6.165 6 8.388 6 11v3.159c0 .538-.214 1.055-.595 1.436L4 17h5m6 0v1a3 3 0 11-6 0v-1m6 0H9" />
</svg>
</button>
<span class="absolute -top-1 -right-1 flex h-3 w-3">
<span class="animate-ping absolute inline-flex h-full w-full rounded-full bg-red-400 opacity-75"></span>
<span class="relative inline-flex rounded-full h-3 w-3 bg-red-500"></span>
</span>
</div>animate-pulse
For skeleton loaders:
<!-- Skeleton card -->
<div class="bg-white rounded-2xl p-6 shadow animate-pulse">
<div class="flex items-center gap-4 mb-4">
<div class="w-12 h-12 bg-gray-200 rounded-full"></div>
<div class="flex-1 space-y-2">
<div class="h-4 bg-gray-200 rounded w-3/4"></div>
<div class="h-3 bg-gray-200 rounded w-1/2"></div>
</div>
</div>
<div class="space-y-3">
<div class="h-3 bg-gray-200 rounded"></div>
<div class="h-3 bg-gray-200 rounded w-5/6"></div>
<div class="h-3 bg-gray-200 rounded w-4/6"></div>
</div>
</div>animate-bounce
For scroll cues and draw attention:
<div class="flex flex-col items-center">
<p class="text-sm text-gray-500 mb-2">Scroll down</p>
<svg class="w-6 h-6 text-gray-400 animate-bounce" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M19 9l-7 7-7-7" />
</svg>
</div>Custom keyframes with @keyframes in v4
Define completely custom animations in your CSS:
@import "tailwindcss";
/* Define the keyframes */
@keyframes fade-in-up {
from {
opacity: 0;
transform: translateY(16px);
}
to {
opacity: 1;
transform: translateY(0);
}
}
@keyframes shimmer {
0% { background-position: -200% center; }
100% { background-position: 200% center; }
}
/* Register as a Tailwind animation token */
@theme {
--animate-fade-in-up: fade-in-up 0.4s ease-out both;
--animate-shimmer: shimmer 1.5s linear infinite;
}Use them in HTML:
<div class="animate-fade-in-up">Fades in and rises</div>
<div class="animate-shimmer bg-[linear-gradient(90deg,#f0f0f0_25%,#e0e0e0_50%,#f0f0f0_75%)] bg-[length:200%_100%] rounded">
Shimmer loading effect
</div>Respecting user motion preferences
Always provide a reduced-motion alternative for users who have enabled "Reduce motion" in their OS accessibility settings.
<!-- motion-safe applies only when prefers-reduced-motion is NOT set -->
<!-- motion-reduce applies only when prefers-reduced-motion IS set -->
<div class="motion-safe:animate-bounce motion-reduce:animate-none">
Bounces for most users, stays still for sensitive users
</div>
<button class="motion-safe:hover:-translate-y-1 motion-safe:transition-transform duration-200">
Lift on hover (only with motion enabled)
</button>A best practice for all animations:
<!-- Good: uses motion-safe to protect sensitive users -->
<div class="motion-safe:transition-all motion-safe:duration-300 hover:shadow-xl hover:-translate-y-1">
Smooth card
</div>Practice
- Build an interactive button that scales down slightly (
active:scale-95) on click and shows a spinner (animate-spin) when in a loading state. - Create a skeleton loader for a blog post card using
animate-pulsewith rectangles for title, author, and body text. - Define a custom
@keyframes slide-in-rightanimation in your CSS and register it as--animate-slide-in-rightin@theme. Use it withanimate-slide-in-righton a notification toast.
<!-- Practical transition examples: buttons, cards, inputs, and icons -->
<!-- 1. Button with color transition -->
<button
type="button"
class="bg-indigo-600 hover:bg-indigo-700 active:bg-indigo-800 active:scale-95 text-white font-semibold px-6 py-3 rounded-xl transition-all duration-150 ease-in-out focus-visible:ring-2 focus-visible:ring-indigo-500 focus-visible:ring-offset-2"
>
Save changes
</button>
<!-- 2. Card with lift effect -->
<div class="bg-white rounded-2xl shadow-md hover:shadow-xl hover:-translate-y-1 transition-all duration-200 ease-out p-6 cursor-pointer">
<h3 class="font-bold text-gray-900">Hoverable card</h3>
<p class="text-gray-500 mt-2 text-sm">Lifts on hover with a smooth shadow transition.</p>
</div>
<!-- 3. Icon rotation on expand -->
<div class="group">
<button
type="button"
class="flex items-center gap-2 font-medium text-gray-700 hover:text-indigo-600 transition-colors"
>
Details
<svg
class="w-4 h-4 transition-transform duration-200 group-hover:rotate-180"
fill="none"
stroke="currentColor"
viewBox="0 0 24 24"
>
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M19 9l-7 7-7-7" />
</svg>
</button>
</div>
<!-- 4. Input focus ring transition -->
<input
type="text"
placeholder="Enter your name"
class="w-full border border-gray-200 rounded-lg px-4 py-2.5 text-gray-900 placeholder-gray-400 outline-none transition-all duration-150 focus:border-indigo-400 focus:ring-2 focus:ring-indigo-500/20"
/>
<!-- 5. Loading spinner animation -->
<div
class="w-8 h-8 rounded-full border-4 border-gray-200 border-t-indigo-600 animate-spin"
role="status"
aria-label="Loading"
></div>
<!-- 6. Pulse skeleton loader -->
<div class="space-y-3">
<div class="h-4 bg-gray-200 rounded animate-pulse w-3/4"></div>
<div class="h-4 bg-gray-200 rounded animate-pulse w-full"></div>
<div class="h-4 bg-gray-200 rounded animate-pulse w-5/6"></div>
</div>
Sign in to track your progress