Animations
In this chapter, you learn how to implement animations with Tailwind CSS.
Transitions
Basics
<!-- Enable transitions -->
<button class="transition bg-blue-500 hover:bg-blue-600">
Transitions automatically
</button>
Transition properties
| Class | Target property |
|---|---|
transition-none | None |
transition-all | All |
transition | Common properties |
transition-colors | Color-related |
transition-opacity | Opacity |
transition-shadow | Shadow |
transition-transform | Transform |
<button class="transition-colors bg-blue-500 hover:bg-blue-600">
Transition colors only
</button>
<div class="transition-transform hover:scale-105">
Transition transform only
</div>
Duration
<div class="transition duration-75">75ms</div>
<div class="transition duration-100">100ms</div>
<div class="transition duration-150">150ms (default)</div>
<div class="transition duration-200">200ms</div>
<div class="transition duration-300">300ms</div>
<div class="transition duration-500">500ms</div>
<div class="transition duration-700">700ms</div>
<div class="transition duration-1000">1000ms</div>
Easing
<div class="transition ease-linear">Linear</div>
<div class="transition ease-in">Slow at the start</div>
<div class="transition ease-out">Slow at the end</div>
<div class="transition ease-in-out">Slow at both ends</div>
Delay
<div class="transition delay-75">75ms delay</div>
<div class="transition delay-100">100ms delay</div>
<div class="transition delay-150">150ms delay</div>
<div class="transition delay-200">200ms delay</div>
<div class="transition delay-300">300ms delay</div>
<div class="transition delay-500">500ms delay</div>
Transform
Scale (enlarge / shrink)
<div class="scale-0">0%</div>
<div class="scale-50">50%</div>
<div class="scale-75">75%</div>
<div class="scale-90">90%</div>
<div class="scale-100">100% (default)</div>
<div class="scale-105">105%</div>
<div class="scale-110">110%</div>
<div class="scale-125">125%</div>
<div class="scale-150">150%</div>
<!-- X axis / Y axis individually -->
<div class="scale-x-50">Horizontal 50%</div>
<div class="scale-y-150">Vertical 150%</div>
Rotate
<div class="rotate-0">0 degrees</div>
<div class="rotate-45">45 degrees</div>
<div class="rotate-90">90 degrees</div>
<div class="rotate-180">180 degrees</div>
<div class="-rotate-45">-45 degrees</div>
Translate
<div class="translate-x-4">16px to the right</div>
<div class="translate-y-4">16px down</div>
<div class="-translate-x-4">16px to the left</div>
<div class="-translate-y-4">16px up</div>
<div class="translate-x-1/2">50% to the right</div>
<div class="-translate-x-1/2 -translate-y-1/2">50% to the upper left on each axis</div>
Skew
<div class="skew-x-6">Skew 6 degrees on the X axis</div>
<div class="skew-y-6">Skew 6 degrees on the Y axis</div>
<div class="-skew-x-6">Skew -6 degrees on the X axis</div>
Transform origin
<div class="origin-center">Center (default)</div>
<div class="origin-top">Top</div>
<div class="origin-top-right">Top right</div>
<div class="origin-right">Right</div>
<div class="origin-bottom-right">Bottom right</div>
<div class="origin-bottom">Bottom</div>
<div class="origin-bottom-left">Bottom left</div>
<div class="origin-left">Left</div>
<div class="origin-top-left">Top left</div>
3D transforms (v4)
3D transforms use the transform property, so GPU acceleration kicks in. However, heavy use of perspective or transform-3d may increase memory consumption. In complex 3D scenes, apply them only to the elements that need them.
In v4, 3D transforms are supported.
Perspective
<!-- Named values -->
<div class="perspective-normal">Standard perspective (500px)</div>
<div class="perspective-midrange">Mid-range perspective (800px)</div>
<!-- Specifying an arbitrary value -->
<div class="perspective-[500px]">500px perspective</div>
<div class="perspective-[1000px]">1000px perspective</div>
The v4 perspective utilities use named values such as perspective-dramatic (100px) and perspective-near (300px). Others include perspective-normal (500px), perspective-midrange (800px), and perspective-distant (1200px). Specify an arbitrary px value with bracket syntax (perspective-[500px]).
3D rotation
<div class="rotate-x-45">Rotate 45 degrees on the X axis</div>
<div class="rotate-y-45">Rotate 45 degrees on the Y axis</div>
<div class="rotate-z-45">Rotate 45 degrees on the Z axis</div>
3D translation
<div class="translate-z-10">Translate on the Z axis</div>
Preserving 3D rendering
<div class="transform-3d">Preserve 3D space</div>
<div class="transform-flat">Make it flat</div>
Backface visibility
<div class="backface-visible">Show the backface</div>
<div class="backface-hidden">Hide the backface</div>
Built-in animations
spin (rotation)
<svg class="animate-spin h-5 w-5">...</svg>
ping (ripple)
<span class="relative flex h-3 w-3">
<span class="animate-ping absolute inline-flex h-full w-full rounded-full bg-blue-400 opacity-75"></span>
<span class="relative inline-flex rounded-full h-3 w-3 bg-blue-500"></span>
</span>
pulse (pulsing)
<div class="animate-pulse bg-gray-200 h-4 rounded-sm"></div>
bounce
<svg class="animate-bounce h-6 w-6">↓</svg>
Controlling animations
Tailwind v4's core built-in animations are the five animate-spin / animate-ping / animate-pulse / animate-bounce / animate-none. Utilities to change the iteration count, direction, or play state are not provided in the core, so specify them individually with arbitrary-property syntax (for complex animations, define --animate-* with @theme as in Customization).
<!-- Iteration count (animate-spin is infinite, so override with an arbitrary property) -->
<div class="animate-spin [animation-iteration-count:1]">1 time</div>
<div class="animate-spin [animation-iteration-count:2]">2 times</div>
<!-- Direction -->
<div class="animate-spin [animation-direction:reverse]">Reverse</div>
<div class="animate-spin [animation-direction:alternate]">Alternate</div>
<!-- Play state (pause on hover) -->
<div class="animate-spin hover:[animation-play-state:paused]">Pause on hover</div>
Practical examples
Hover animations
<!-- Card hover -->
<div class="transition-all duration-300 hover:scale-105 hover:shadow-xl">
<div class="bg-white rounded-lg shadow-sm p-6">
Card content
</div>
</div>
<!-- Button hover -->
<button class="relative overflow-hidden bg-blue-500 text-white px-6 py-3 rounded-lg group">
<span class="relative z-10">Hover me</span>
<span class="absolute inset-0 bg-blue-600 transform scale-x-0 group-hover:scale-x-100 transition-transform origin-left"></span>
</button>
<!-- Image hover -->
<div class="overflow-hidden rounded-lg">
<img
src="..."
class="transition-transform duration-500 hover:scale-110"
/>
</div>
Fade-in
<!-- Implemented with a CSS animation -->
<style>
@keyframes fade-in {
from { opacity: 0; transform: translateY(20px); }
to { opacity: 1; transform: translateY(0); }
}
</style>
<div class="animate-[fade-in_0.5s_ease-out]">
Fade-in content
</div>
Loading spinner
<div class="flex items-center justify-center">
<div class="animate-spin rounded-full h-12 w-12 border-4 border-blue-500 border-t-transparent"></div>
</div>
<!-- Dot loading -->
<div class="flex gap-1">
<div class="w-2 h-2 bg-blue-500 rounded-full animate-bounce [animation-delay:-0.3s]"></div>
<div class="w-2 h-2 bg-blue-500 rounded-full animate-bounce [animation-delay:-0.15s]"></div>
<div class="w-2 h-2 bg-blue-500 rounded-full animate-bounce"></div>
</div>
Skeleton loading
<div class="animate-pulse space-y-4">
<div class="h-4 bg-gray-200 rounded-sm w-3/4"></div>
<div class="h-4 bg-gray-200 rounded-sm"></div>
<div class="h-4 bg-gray-200 rounded-sm w-5/6"></div>
<div class="flex gap-4">
<div class="h-12 w-12 bg-gray-200 rounded-full"></div>
<div class="flex-1 space-y-2 py-1">
<div class="h-4 bg-gray-200 rounded-sm w-1/2"></div>
<div class="h-4 bg-gray-200 rounded-sm w-1/4"></div>
</div>
</div>
</div>
Accordion
<details class="group border border-gray-300 rounded-lg">
<summary class="flex justify-between items-center p-4 cursor-pointer">
<span>Accordion title</span>
<svg class="w-5 h-5 transition-transform duration-200 group-open:rotate-180">
▼
</svg>
</summary>
<div class="overflow-hidden transition-all duration-300 max-h-0 group-open:max-h-screen">
<div class="p-4 pt-0">
Accordion content...
</div>
</div>
</details>
Modal animation
The open: variant applies only to elements that can have an open attribute, such as <details> and <dialog>. Use a <dialog> element and toggle the display based on the presence of the open attribute.
<!-- Overlay (the dialog itself doubles as the overlay) -->
<dialog class="group fixed inset-0 bg-black/50 transition-opacity duration-300
opacity-0 open:opacity-100">
<!-- Modal -->
<div class="fixed inset-0 flex items-center justify-center p-4">
<div class="bg-white rounded-xl shadow-xl max-w-lg w-full
transform transition-all duration-300
scale-95 opacity-0 group-open:scale-100 group-open:opacity-100">
Modal content
</div>
</div>
</dialog>
Underline animation
<a href="#" class="relative inline-block group">
Link text
<span class="absolute bottom-0 left-0 w-full h-0.5 bg-blue-500
transform scale-x-0 group-hover:scale-x-100
transition-transform duration-300 origin-left"></span>
</a>
Card flip
<div class="perspective-[1000px] w-64 h-40">
<div class="relative w-full h-full transition-transform duration-500
transform-3d hover:rotate-y-180">
<!-- Front -->
<div class="absolute inset-0 backface-hidden bg-blue-500 text-white
rounded-lg flex items-center justify-center">
Front
</div>
<!-- Back -->
<div class="absolute inset-0 backface-hidden bg-purple-500 text-white
rounded-lg flex items-center justify-center rotate-y-180">
Back
</div>
</div>
</div>
Animation best practices
1. Be mindful of performance
<!-- Good: use transform / opacity (GPU acceleration) -->
<div class="transition-transform hover:translate-x-2">...</div>
<!-- Avoid: use left / top (triggers reflow) -->
<div class="transition-all hover:left-2">...</div>
2. Consider accessibility
prefers-reduced-motion is a media query that corresponds to the OS "reduce motion" setting. Use it to accommodate users with vestibular disorders and users who prefer less motion-heavy UI.
<!-- motion-safe: animate only when motion is allowed -->
<div class="motion-safe:animate-bounce motion-reduce:animate-none">
...
</div>
<!-- motion-reduce: an alternative for reduced motion -->
<div class="transition-transform motion-safe:hover:scale-105 motion-reduce:hover:opacity-80">
Indicate the change with opacity instead of scale
</div>
On pages that use a lot of animation, we recommend making motion-safe: your default. This way, users with the reduce-motion setting get the no-animation state by default.
3. Appropriate durations
<!-- Micro-interactions: 100-200ms -->
<button class="transition-colors duration-150">...</button>
<!-- Page transitions / modals: 200-300ms -->
<div class="transition-all duration-300">...</div>
<!-- Emphasis: 300-500ms -->
<div class="transition-transform duration-500">...</div>
Summary
- Enable transitions with
transition - Customize with
duration-*/ease-*/delay-* - Transform with
scale-*/rotate-*/translate-* - v4 supports 3D transforms
- Built-in animations such as
animate-spin/animate-pulse - Support accessibility with
motion-safe:/motion-reduce:
What to read next
- Customization — define your own animations with
@theme - Practical project — integrate animations in a landing page build
- Tailwind CSS Guide table of contents — back to the structure of all 18 chapters