Skip to content
HyperUI

No results found.

Back to blog
2 min read

How to Add Animation Duration, Delay and Easing Support to Tailwind CSS

Tailwind CSS still doesn't ship dedicated animation duration, delay, and easing utilities. Here's a clean v4 approach.

Live Demo

 

Tailwind CSS doesn’t include first-class duration and delay utilities for animations.

The demo uses the following classes:

  • Duration: animate-duration-150, animate-duration-300, animate-duration-700, animate-duration-3000
  • Delay: animate-delay-150, animate-delay-300, animate-delay-700, animate-delay-3000
  • Easing: animate-ease-in, animate-ease-out, animate-ease-in-out, animate-ease-[cubic-bezier(0.9,-0.7,0.1,1.7)]
  • Base animation: animate-pulse

If you want reusable classes that feel native, use @utility with the animate-* naming style.

Add Classes in Tailwind CSS

Add this to a stylesheet that Tailwind processes (for example, your global CSS):

@utility animate-duration-* {
  --animate-duration: --value(integer)ms;
  --animate-duration: --value([time]);
  animation-duration: var(--animate-duration);
}

@utility animate-delay-* {
  --animate-delay: --value(integer)ms;
  --animate-delay: --value([time]);
  animation-delay: var(--animate-delay);
}

@utility animate-ease-* {
  --animate-easing: --value(--ease-*);
  --animate-easing: --value([*]);
  animation-timing-function: var(--animate-easing);
}

This gives you classes like:

  • animate-duration-<number>
  • animate-delay-<number>
  • animate-ease-<in, out, in-out>
  • animate-ease-[cubic-bezier(.17,.67,.83,.67)]
  • animate-ease-[cubic-bezier(0.9,-0.7,0.1,1.7)]

Classes extend from the existing transition- classes:

Arbitrary values also work:

  • animate-duration-[1.2s]
  • animate-delay-[250ms]
  • animate-ease-[cubic-bezier(.17,.67,.83,.67)]

The live demo includes this exact custom easing class:

  • animate-ease-[cubic-bezier(0.9,-0.7,0.1,1.7)]

Why This Works

The custom utilities set both CSS variables and CSS properties:

  • animate-duration-* maps numeric suffixes like 150 to 150ms, then sets --animate-duration and animation-duration
  • animate-delay-* maps numeric suffixes like 300 to 300ms, then sets --animate-delay and animation-delay
  • animate-ease-* maps easing tokens like in, out, and in-out, and also accepts arbitrary timing functions like cubic-bezier(...)

Existing classes like animate-spin keep working, but now they can have overrides with the added utility classes.

Quick Example

<div class="animate-duration-700 animate-delay-300 animate-ease-in-out animate-spin">...</div>

This also works:

<div class="animate-delay-700 animate-duration-[1500ms] animate-ease-in-out animate-spin">...</div>

The recommended approach in this post is the @utility setup above because it gives you reusable, consistent classes.

For one-off cases, use arbitrary utility values with the same class names:

  • animate-duration-[5s]
  • animate-delay-[250ms]
  • animate-ease-[cubic-bezier(.17,.67,.83,.67)]

If needed, you can also use arbitrary properties directly:

  • [animation-duration:5s]
  • [animation-delay:0.25s]
  • [animation-timing-function:cubic-bezier(.17,.67,.83,.67)]

For custom easing curves, animate-ease-[...] is usually the clearest option.

Example from the live demo:

  • animate-ease-[cubic-bezier(0.9,-0.7,0.1,1.7)]

Example variable override:

  • [--animate-easing:cubic-bezier(.17,.67,.83,.67)]
HyperUX — Behavior-first Alpine.js patterns you can copy, adapt, and ship.