0Pricing
Tailwind CSS Academy · Lesson

Custom Animations in Config

Define custom keyframes and animation utilities in tailwind.config.js and reference them with animate-* classes in your markup.

Custom Animations in Config is a free Tailwind CSS Academy lesson on CoddyKit — lesson 4 of 4. You can read the complete lesson below for free — then practise it hands-on in the browser with a built-in code editor and a 24/7 AI tutor. It is part of the Tailwind CSS Academy learning path, one of 4 lessons in the course, and your progress syncs across the web and the CoddyKit app.

Beyond the Four Built-In Animations

Tailwind's four built-in animations cover common loading and attention patterns, but real projects often need additional animations: fade-in effects for content entrance, slide-in sidebars, shake effects for form errors, or custom pulse variations. By adding entries to theme.extend.keyframes and theme.extend.animation in your config, you create new animate-* utilities that work exactly like the built-ins.

Defining Custom Keyframes

Add custom CSS keyframes in theme.extend.keyframes. Each key in the object becomes a keyframe name. The value is an object where keys are percentage strings or 'from'/'to' keywords, and values are CSS property objects. Tailwind converts this object notation to the @keyframes CSS rule in the compiled output. You can use any animatable CSS property inside the keyframe steps.

// tailwind.config.js
module.exports = {
  theme: {
    extend: {
      keyframes: {
        // Fade in from transparent
        'fade-in': {
          '0%':   { opacity: '0' },
          '100%': { opacity: '1' },
        },
        // Slide up from below
        'slide-up': {
          '0%':   { opacity: '0', transform: 'translateY(16px)' },
          '100%': { opacity: '1', transform: 'translateY(0)' },
        },
        // Shake (for errors)
        'shake': {
          '0%, 100%': { transform: 'translateX(0)' },
          '20%, 60%': { transform: 'translateX(-8px)' },
          '40%, 80%': { transform: 'translateX(8px)' },
        },
      },
    },
  },
};

Registering Custom Animation Utilities

After defining keyframes, register them as named animations in theme.extend.animation. The value is a CSS animation shorthand string: name duration timing-function delay iteration-count direction fill-mode. The name must match a keyframe defined in theme.extend.keyframes. Tailwind generates an animate-{key} utility class for each entry.

// tailwind.config.js
module.exports = {
  theme: {
    extend: {
      keyframes: { /* ... defined above ... */ },
      animation: {
        'fade-in':  'fade-in 0.3s ease-out',
        'slide-up': 'slide-up 0.4s ease-out',
        'shake':    'shake 0.5s ease-in-out 1',
        // Multiple values
        'fade-in-slow': 'fade-in 1s ease-in-out',
        // Infinite loop
        'fade-in-out': 'fade-in 1.5s ease-in-out infinite alternate',
      },
    },
  },
};

Using Custom Animations in HTML

Once registered in the config, custom animations are available as animate-{key} classes in your HTML. They work exactly like the built-in animate utilities and can be combined with responsive prefixes, state variants, and the motion-reduce: variant for accessibility. The animation plays when the class is present and stops when the class is removed — perfect for JavaScript-triggered entrance effects.

<!-- Fade in a card on page load -->
<div class="animate-fade-in bg-white rounded-xl p-6 shadow">
  Content fades in smoothly
</div>

<!-- Slide up with a slight delay using inline style -->
<div class="animate-slide-up" style="animation-delay: 150ms">
  Content slides up after 150ms delay
</div>

<!-- Shake an input on validation error -->
<input
  id="email"
  class="border border-red-500 animate-shake focus:outline-none px-4 py-2 rounded-lg w-full"
  type="email"
  placeholder="Invalid email"
/>

Entrance Animations for Content

Entrance animations (fade-in, slide-up, scale-in) make content feel alive as it appears. They are most effective when content enters the viewport for the first time — on page load or when a section becomes visible during scrolling. Keep entrance durations between 200–400ms and always pair them with appropriate easing. Use the Intersection Observer API in JavaScript to trigger animations when elements scroll into view.

// JavaScript: trigger animation when element enters viewport
const observer = new IntersectionObserver((entries) => {
  entries.forEach((entry) => {
    if (entry.isIntersecting) {
      entry.target.classList.add('animate-slide-up');
      observer.unobserve(entry.target); // animate once
    }
  });
}, { threshold: 0.1 });

document.querySelectorAll('.animate-on-scroll').forEach((el) => {
  observer.observe(el);
});

<!-- HTML: starts invisible, gets class on scroll -->
<div class="opacity-0 animate-on-scroll">Animated on scroll</div>

More Complex Keyframe Examples

Complex animations use more keyframe percentage steps to create multi-stage motion. A heartbeat animation scales up then back down with a double-beat rhythm. A flash animation briefly highlights an element with a background color. A slide-in-right animation translates and fades simultaneously. These compound animations require multiple percentage steps to describe each stage of the motion precisely.

// tailwind.config.js
keyframes: {
  'heartbeat': {
    '0%, 100%': { transform: 'scale(1)' },
    '14%':      { transform: 'scale(1.3)' },
    '28%':      { transform: 'scale(1)' },
    '42%':      { transform: 'scale(1.3)' },
    '70%':      { transform: 'scale(1)' },
  },
  'flash': {
    '0%, 50%, 100%': { opacity: '1' },
    '25%, 75%':      { opacity: '0.25' },
  },
  'slide-in-right': {
    '0%':   { opacity: '0', transform: 'translateX(32px)' },
    '100%': { opacity: '1', transform: 'translateX(0)' },
  },
},
animation: {
  'heartbeat':      'heartbeat 1.2s ease-in-out infinite',
  'flash':          'flash 0.5s ease-in-out',
  'slide-in-right': 'slide-in-right 0.3s ease-out',
}

Animation Fill Mode

The CSS animation-fill-mode property controls what happens before the animation starts (if there is a delay) and after it ends. forwards keeps the element in its final state after the animation ends — crucial for one-shot entrance animations where you want the element to stay visible. backwards applies the first frame during the delay period so elements start in the correct initial state. Include fill mode in your animation shorthand string.

// animation shorthand: name | duration | easing | delay | count | fill-mode
animation: {
  // 'forwards' keeps final state (element stays visible after fading in)
  'fade-in': 'fade-in 0.3s ease-out forwards',

  // 'backwards' applies frame-0 during delay (starts transparent during delay)
  'fade-in-delayed': 'fade-in 0.4s ease-out 0.2s backwards',

  // No fill mode: jumps back to initial state after animation
  'shake': 'shake 0.5s ease-in-out',
}

Pausing and Resuming Animations

You can pause a Tailwind animation using animation-play-state: paused. Add a custom utility class or use an arbitrary value: [animation-play-state:paused]. This is useful for pausing loading animations when a page is not visible (using the Page Visibility API) or pausing hover animations to give users time to interact with content. Tailwind does not have a built-in pause utility, but arbitrary CSS or a custom utility covers this.

@layer utilities {
  .animation-paused { animation-play-state: paused; }
  .animation-running { animation-play-state: running; }
}

<!-- Pause animation on hover -->
<div class="animate-pulse hover:animation-paused bg-gray-200 rounded h-4 w-48">
  Pauses pulsing when hovered
</div>

<!-- JavaScript: pause when page is hidden -->
document.addEventListener('visibilitychange', () => {
  const spinners = document.querySelectorAll('.animate-spin');
  spinners.forEach(el => {
    el.style.animationPlayState = document.hidden ? 'paused' : 'running';
  });
});

Respecting Reduced Motion in Custom Animations

Every custom animation you create should also have a motion-reduce: treatment. The simplest approach is motion-reduce:animate-none to disable the animation entirely. For entrance animations where you still want the final state to show, use motion-reduce:opacity-100 alongside motion-reduce:animate-none to ensure the element is visible even when the fade-in animation is skipped.

<!-- Accessible entrance animation -->
<div
  class="
    opacity-0 animate-fade-in
    motion-reduce:opacity-100 motion-reduce:animate-none
  "
>
  Content visible immediately for reduced-motion users,
  fades in for others
</div>

<!-- Accessible slide-up -->
<div
  class="
    opacity-0 translate-y-4 animate-slide-up
    motion-reduce:opacity-100 motion-reduce:translate-y-0 motion-reduce:animate-none
  "
>
  Reduced motion: appears instantly at final position
</div>

Third-Party Animation Libraries With Tailwind

For more sophisticated animations — spring physics, scroll-driven animations, or complex choreography — third-party libraries like tailwindcss-animate, Framer Motion (React), or GSAP integrate well with Tailwind. Tailwindcss-animate provides a rich set of pre-built keyframes (accordion-down, accordion-up, fade-in, etc.) as a plugin, instantly extending your available animate-* utilities. It is used by default in shadcn/ui components.

// Install tailwindcss-animate
// npm install tailwindcss-animate

// tailwind.config.js
module.exports = {
  plugins: [
    require('tailwindcss-animate'),
  ],
};

<!-- Now you have many new animate-* utilities -->
<div class="animate-in fade-in slide-in-from-bottom-4 duration-300">
  Animated with tailwindcss-animate
</div>

<div class="animate-out fade-out slide-out-to-top-4 duration-200">
  Exit animation
</div>

Debugging Custom Animations

Use Chrome DevTools' Animations panel to inspect and debug custom animations. Select the element in the Elements panel, then open the Animations panel. Run the animation and you will see the keyframe curve visualized with timing markers. You can slow down playback to 0.25x speed to carefully inspect each stage. The panel also lets you replay animations on demand, which is far easier than refreshing the page to catch entrance animations.

Quick Check

Test your understanding of custom animations in the Tailwind config.

Lesson Recap

In this lesson you learned: define custom keyframes in theme.extend.keyframes and register them as utilities in theme.extend.animation, use animation fill-mode forwards for one-shot entrance animations that should maintain their final state, and always pair custom animations with motion-reduce:animate-none for accessibility. This completes the Animations and Transitions course — next we build polished component patterns.

Frequently asked questions

Is the “Custom Animations in Config” lesson free?

Yes — the full text of “Custom Animations in Config” is free to read here on the web, and the Tailwind CSS Academy course includes 4 lessons in total. To practise it interactively (a built-in code editor and a 24/7 AI tutor) and unlock the rest of the Tailwind CSS Academy course, upgrade to CoddyKit PRO.

What will I learn in “Custom Animations in Config”?

Define custom keyframes and animation utilities in tailwind.config.js and reference them with animate-* classes in your markup. You practise Tailwind CSS Academy with hands-on code you run directly in the browser, and a 24/7 AI tutor answers your questions as you work through the lesson.

Do I need any experience to start Tailwind CSS Academy?

No prior experience is required. Tailwind CSS Academy on CoddyKit is structured for beginners through advanced learners; this is — lesson 4 of 4, so you can start here or from the beginning and move at your own pace.

How long does the “Custom Animations in Config” lesson take?

Most CoddyKit lessons take about 5–10 minutes. Each one is bite-sized and interactive, so you make steady progress and pick up exactly where you left off across the web and the app.

Can I write and run code in this Tailwind CSS Academy lesson?

Yes. Every Tailwind CSS Academy lesson includes a built-in code editor, so you write and run real code right in your browser and get instant AI feedback — no local setup required.

All lessons in this course

  1. Transition Utilities
  2. Duration and Easing
  3. Built-In Keyframe Animations
  4. Custom Animations in Config
← Back to Tailwind CSS Academy