Skip to main content

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​

ClassTarget property
transition-noneNone
transition-allAll
transitionCommon properties
transition-colorsColor-related
transition-opacityOpacity
transition-shadowShadow
transition-transformTransform
<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 GPU, but watch out for memory consumption

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>
When to use named perspective values versus arbitrary values

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>

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>
Make motion-safe the default

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: