Motion tells users that something responded to them: a button that lifts on hover, a menu that fades in, a spinner that turns while data loads. In this lesson you will learn how to transition property changes smoothly, move, scale and rotate elements, use and define keyframe animations, and respect users who prefer reduced motion.
A transition animates the change between two states. Add transition, then change a property with a variant:
<a class="text-gray-600 transition-colors duration-200 hover:text-blue-600">Link</a>
<div class="opacity-0 transition-opacity delay-150 duration-500 group-hover:opacity-100">Reveal</div>| Utility | Effect |
|---|---|
| transition | Animates colors, opacity, shadow, transform and filter |
| transition-colors, -opacity, -shadow, -transform, -all | Narrow (or widen) the animated properties |
| duration-<ms>, delay-<ms> | Length and delay; the default duration is 150ms |
| ease-in, ease-out, ease-in-out, ease-linear | Timing curve; ease-out feels fastest for hover |
Transitions only run between two computed values, so hidden to block cannot animate by default — see the entry animation section for the fix.
Transforms move, resize and rotate without affecting layout; in v4 each is an independent property, so they compose freely:
<img class="transition-transform duration-300 hover:scale-110" />
<svg class="rotate-90 transition-transform group-open:rotate-180">Chevron</svg>
<div class="absolute top-1/2 left-1/2 -translate-x-1/2 -translate-y-1/2">Centred</div>scale-<n> (scale-95, scale-105), or scale-x-* / scale-y-* for one axis.rotate-<deg> and -rotate-<deg>; v4 adds 3D rotate-x-*, rotate-y-* and perspective-*.translate-x-* / translate-y-* accept the spacing scale, fractions and full.origin-* sets the pivot; transform-gpu forces hardware acceleration; transform-none resets.Four keyframe animations ship with Tailwind:
<svg class="size-6 animate-spin text-blue-600">...</svg> <!-- spinner -->
<span class="size-3 animate-ping rounded-full bg-red-400"></span> <!-- live dot -->
<div class="h-4 w-48 animate-pulse rounded bg-gray-200"></div> <!-- skeleton -->
<div class="animate-bounce">Scroll down</div>animate-none stops an animation at a state, for example hover:animate-none.
New animations are --animate-* variables, with keyframes inside the same @theme block so they are emitted only when used:
@theme {
--animate-fade-in: fade-in 0.3s ease-out both;
--animate-slide-up: slide-up 0.4s cubic-bezier(0.16, 1, 0.3, 1) both;
@keyframes fade-in {
from { opacity: 0; }
to { opacity: 1; }
}
@keyframes slide-up {
from { transform: translateY(1rem); opacity: 0; }
to { transform: translateY(0); opacity: 1; }
}
}This produces animate-fade-in and animate-slide-up, usable with every variant. Custom curves stored as --ease-* variables become ease-* utilities.
Elements that start as display: none — dialogs, popovers, dropdowns — need two extra utilities. transition-discrete lets display take part in the transition, and the starting: variant (@starting-style) defines where the animation begins:
<div popover class="opacity-0 transition-all transition-discrete duration-300
open:opacity-100 starting:open:opacity-0">
Fades in and out
</div>Finally, gate non-essential motion behind the operating-system setting: motion-safe:animate-bounce runs only when reduced motion is not requested, and motion-reduce:transition-none switches transitions off when it is, as in the sample button above.
transition utility, so the hover change snaps instantly.width or height instead of scale or translate, which forces layout on every frame.animate-bounce running without motion-safe:, which some users find unpleasant.Which utility makes a hover color change fade over 200ms instead of switching instantly?
transition plus duration-*, ease-* and delay-* animate state changes; prefer specific groups like transition-colors.scale, rotate, translate) are independent properties in v4 and never affect layout.animate-spin, animate-ping, animate-pulse and animate-bounce cover loading and attention states.--animate-* variables with @keyframes inside @theme.transition-discrete plus starting: animates entry from display: none; gate motion with motion-safe:.Next lesson: Customizing the Theme and Config — adjust fonts, colors and spacing to match your brand.