@keyframes & --animate-* namespace

[check: …]
📖 Pair this live Tailwind demo with the companion guide (.md) — this page is the rendered ground truth. ↗ Animations are the second half of motion: pair with transitions & timing (state-driven easing) and @property directive (the trick that makes CSS custom properties *animatable* — required for gradient-position loops).

the --animate-* namespace: theme var → animation utility

In Tailwind v4, every animation is just a CSS custom property. The --animate-* namespace maps directly to the CSS animation: shorthand. Add --animate-wiggle: wiggle 1s ease-in-out infinite to @theme, define @keyframes wiggle, and the utility animate-wiggle exists automatically. No plugin, no tailwind.config.js theme.extend.animation function — v4 collapsed all of that into theme variables.

piecewhere it liveswhat it does
--animate-wiggle @theme { ... } block registers the utility animate-wiggle; its value becomes the animation: declaration
@keyframes wiggle top-level CSS (or nested in @theme) defines the named keyframe timeline the animation references
animate-wiggle markup class applies animation: var(--animate-wiggle) to the element
animate-[wiggle_1s_linear] arbitrary value one-off animation without registering a theme token
animate-(--my-anim) shorthand reads any CSS var: equivalent to animate-[var(--my-anim)]

1 · custom animations — toggle each on/off

Three animations defined in <style type="text/tailwindcss"> above: --animate-wiggle (rotation), --animate-float (vertical bob), --animate-gradient (background-position loop for gradient text). Click a button to toggle the utility class — the gold-check re-runs and proves animationName flips between the keyframe name and none.

W

animate-wiggle

wiggle 1s ease-in-out infinite · rotate(-3deg) ↔ rotate(3deg)

animate-float

float 3s ease-in-out infinite · translateY(0) ↔ translateY(-14px)

gradient

animate-gradient

gradient 3s ease infinite · background-position 0% ↔ 100% (needs bg-[length:200%_auto])

wiggle.animationName: —

2 · built-in animations (shipped in v4's default theme)

These five ship out of the box. Each is itself just a --animate-* token + @keyframes — the same mechanism your custom animations use. animate-none sets animation: none to kill inherited/looping animations.

animate-spin

animate-ping

animate-pulse

animate-bounce

utility--animate-* valuetypical use
animate-spinspin 1s linear infiniteloading spinners
animate-pingping 1s cubic-bezier(0,0,.2,1) infinitenotification ripples, radar pings
animate-pulsepulse 2s cubic-bezier(.4,0,.6,1) infiniteskeleton loaders, opacity breathing
animate-bouncebounce 1s infinite"scroll down" arrows, attention
animate-nonenonedisable animation (overrides)

animation shorthand & intent → pattern

The --animate-* value IS the CSS animation: shorthand, so any of these 8 sub-properties can appear in it, space-separated, in any order (time values are parsed as duration then delay).

shorthand slotexample valuenotes
namewigglemust match an @keyframes name
duration1s / 300msfirst time value
timing-functionease-in-out, linear, cubic-bezier(0,0,.2,1)easing per the whole animation
delay0s, 200mssecond time value
iteration-countinfinite, 3how many times to run
directionnormal, alternate, reversealternate ping-pongs each iteration
fill-modenone, forwards, backwards, bothforwards holds the last keyframe
play-staterunning, pausedpause on hover: hover:[animation-play-state:paused]
intentpattern
register a reusable animation@theme { --animate-x: x 1s ...; } @keyframes x { ... }
one-off animation (no theme token)class="animate-[wiggle_1s_ease-in-out_infinite]"
read animation from any CSS varclass="animate-(--my-anim)"
pause on hoverhover:[animation-play-state:paused]
respect reduced-motion preferencemotion-safe:animate-spin / motion-reduce:animate-none
run N times then stop--animate-pop: pop .3s ease 0s 2 forwards;