Just-add-water CSS animation. A cross-browser library of 97 ready-to-use CSS animations — drop in a stylesheet, add two class names, and any element springs to life.
Animate.css is pure CSS. There is nothing to initialize, no runtime dependency, no framework to learn. If you can write HTML, you can use it today.
1. Quick Start
Add the minified stylesheet from a CDN to your page's <head>:
<link
rel="stylesheet"
href="https://cdnjs.cloudflare.com/ajax/libs/animate.css/4.1.1/animate.min.css"
/>
Give any element the class animate__animated plus any animation class (all animation classes use the animate__ prefix):
<h1 class="animate__animated animate__bounce">An animated element</h1>
Reload the page. The <h1> bounces. That's the entire API.
Expected result: the heading plays a one-second "bounce" on page load
and stays in place afterward.
Tip
You always need two classes:
animate__animated(the enabler that sets duration, fill-mode, and the base transform hooks) plus exactly one animation class (for exampleanimate__fadeIn). Neither class does anything useful on its own.
What to try next:
- Browse the full Animation Catalog in section 6
- Customize speed and delay with Utility Classes in section 5
- See JavaScript Integration in section 8 for event-driven animations
2. Installation
System requirements
| Requirement | Value |
|---|---|
| Browser | Any evergreen browser (> 3% marketshare, last 2 versions) |
| JavaScript | Not required |
| Build tools | Not required (optional for npm/bundler workflows) |
Installation methods
CDN (zero-install)
<link
rel="stylesheet"
href="https://cdnjs.cloudflare.com/ajax/libs/animate.css/4.1.1/animate.min.css"
/>
Drop this into <head>. No build step, no bundler, no package manager.
npm
npm install animate.css --save
added 1 package, and audited N packages in Xs
Yarn
yarn add animate.css
Yarn only helps if you are already using a bundler (Webpack, Vite, Parcel, Rollup, esbuild). If you are not, use the CDN method.
Import in JS
import 'animate.css';
This pulls in animate.css (the main field of package.json) — the prefixed, unminified build. If your bundler minifies CSS for you, this is the file you want.
Import the minified build
import 'animate.css/animate.min.css';
Use this if your pipeline does not minify CSS automatically. All three distributed files (animate.css, animate.min.css, animate.compat.css) are shipped in the npm package.
Important
The npm package ships three CSS files at the package root:
animate.css(prefixed, unminified),animate.min.css(prefixed, minified, the production build), andanimate.compat.css(minified, no prefix — the v3 drop-in replacement). Pick exactly one.
Which file do I import?
| File | Prefix | Minified | Use when |
|---|---|---|---|
animate.css |
animate__ |
no | Development, or when your pipeline minifies CSS |
animate.min.css |
animate__ |
yes | Production — the CDN default |
animate.compat.css |
none | yes | Migrating from v3.x where your HTML already uses unprefixed classes (bounce, fadeIn, ...) |
Post-install verification
After including the stylesheet, verify it works:
<p class="animate__animated animate__fadeIn">If you see this fade in, it works.</p>
Expected result: the paragraph fades in over 1 second on page load.
Warning
The
prefers-reduced-motionmedia query is respected in all three builds. If your OS has "reduce motion" enabled, animations complete in 1ms — effectively instant. This is by design, not a bug. See section 10 for details.
3. Configuration
Animate.css uses three CSS custom properties for runtime configuration. No config files, no environment variables, no build-time settings for consumers.
3.1 The three custom properties
| Property | Default | What it controls |
|---|---|---|
--animate-duration |
1s |
Base duration for .animate__animated and all speed utilities |
--animate-delay |
1s |
Base delay for every animate__delay-* class |
--animate-repeat |
1 |
Base iteration count for every animate__repeat-* class (not animate__infinite) |
Change any of them at any scope (:root, a component class, an element id, inline style) and every child picks up the new value.
/* Global slow-motion */
:root { --animate-duration: 2s; }
/* Marketing section: snappy */
.marketing-hero { --animate-duration: 400ms; }
From JavaScript:
// Half-speed everywhere
document.documentElement.style.setProperty('--animate-duration', '2s');
// Double-speed for this element only
myEl.style.setProperty('--animate-duration', '0.5s');
Tip
Scope overrides to specific components rather than
:rootwhen possible. A global--animate-duration: 200msmakes every animation on the page snappy, which may not be desirable for exit animations that need time to read.
3.2 The animate__ prefix
Since v4.0.0, every class name is namespaced with animate__. A class that was called bounce in v3 is now animate__bounce. This eliminates collisions with Bootstrap, Tailwind, Bulma, and any project that used generic names like .fadeIn.
<!-- v3 and earlier (still works via animate.compat.css) -->
<div class="animated bounce">...</div>
<!-- v4+ default -->
<div class="animate__animated animate__bounce">...</div>
3.3 The two-class pattern
Every animation uses the same two-class recipe:
<div class="animate__animated animate__<NAME>">...</div>
animate__animated— wires up the shared machinery:animation-duration: var(--animate-duration)andanimation-fill-mode: both. Without it, nothing animates.animate__<NAME>— picks which keyframe runs. For example,animate__fadeInLeft,animate__bounceOutUp, oranimate__rubberBand.
Note
Because
animation-fill-mode: bothis the default, an element animated withanimate__fadeOutstays invisible after the animation finishes. It does not re-appear. This is by design — see section 9 for patterns that work with this behavior.
For the full configuration reference (build-time prefix customization, per-animation duration overrides), see the Admin Guide.
4. Core Usage
4.1 Animate on page load
Just add the classes in your HTML. The browser plays the animation as soon as the element renders.
<header class="animate__animated animate__fadeInDown">
Welcome to the show.
</header>
4.2 Trigger an animation on a user action
Flip classes on with JavaScript.
<button id="btn">Click me</button>
<div id="target">Surprise</div>
<script>
const btn = document.getElementById('btn');
const target = document.getElementById('target');
btn.addEventListener('click', () => {
target.classList.add('animate__animated', 'animate__tada');
});
</script>
4.3 Play an animation and clean up when it ends
The animationend event fires once per run. Remove the classes so the element can animate again next time.
const el = document.querySelector('.my-element');
el.classList.add('animate__animated', 'animate__bounce');
el.addEventListener('animationend', () => {
el.classList.remove('animate__animated', 'animate__bounce');
}, { once: true });
Note
The
{ once: true }listener option matters — without it, if you re-add the classes later, the same handler fires a second time.
4.4 A reusable "play once" helper
A utility function that wraps the add-animate-remove cycle in a Promise:
const animateCSS = (element, animation, prefix = 'animate__') =>
new Promise((resolve) => {
const animationName = `${prefix}${animation}`;
const node = document.querySelector(element);
node.classList.add(`${prefix}animated`, animationName);
function handleAnimationEnd(event) {
event.stopPropagation();
node.classList.remove(`${prefix}animated`, animationName);
resolve('Animation ended');
}
node.addEventListener('animationend', handleAnimationEnd, { once: true });
});
// Usage
animateCSS('.my-element', 'bounce');
animateCSS('.my-element', 'bounce').then(() => {
// something else after the animation finishes
});
4.5 Animate an element out before removing it
Because exit animations end with opacity: 0 (or equivalent), you can chain a real DOM removal on animationend:
function dismiss(el) {
el.classList.add('animate__animated', 'animate__fadeOutUp');
el.addEventListener('animationend', () => el.remove(), { once: true });
}
4.6 Loop an animation
<span class="animate__animated animate__pulse animate__infinite">NEW</span>
Warning
Infinite animations pull the eye constantly and rapidly become annoying. Use them only for genuinely transient UI (a live-indicator dot on a dashboard, a spinner during loading) and remove them the moment the condition ends.
4.7 Use @keyframes directly in your own CSS
You do not have to use the animate.css classes at all. The @keyframes are globally defined and can be referenced from your own rules:
.my-element {
animation-name: bounce; /* referenced from animate.css */
animation-duration: 2s; /* YOU must set a duration */
animation-fill-mode: both; /* typical */
}
Caution
Some animations depend on properties set by the wrapper class (
.animate__bouncesetstransform-origin: center bottom,.animate__flipInXsetsbackface-visibility: visible !important,.animate__hingesets a 2x duration). If you reference only the@keyframesand skip the class, those properties are missing and the animation will look subtly or dramatically wrong. When in doubt, use the class.
5. Utility Classes
Utility classes live alongside the animation classes on the same element. All of them key off the same three CSS custom properties so you can override behavior globally or per-element.
5.1 Delay classes
Put off the animation's start by a multiple of --animate-delay (default 1s).
<div class="animate__animated animate__bounce animate__delay-2s">Bounces after 2 seconds</div>
| Class | Default delay | Formula |
|---|---|---|
animate__delay-1s |
1s |
var(--animate-delay) |
animate__delay-2s |
2s |
calc(var(--animate-delay) * 2) |
animate__delay-3s |
3s |
calc(var(--animate-delay) * 3) |
animate__delay-4s |
4s |
calc(var(--animate-delay) * 4) |
animate__delay-5s |
5s |
calc(var(--animate-delay) * 5) |
Override the delay base globally or per element:
/* All delay classes wait 2x longer */
:root { --animate-delay: 2s; }
/* Only this element starts after 0.5s */
.hero { --animate-delay: 0.5s; }
5.2 Speed classes
Bend duration without changing the animation's rhythm.
<div class="animate__animated animate__bounce animate__faster">Fast bounce</div>
| Class | Default duration | Formula |
|---|---|---|
animate__slower |
3s |
calc(var(--animate-duration) * 3) |
animate__slow |
2s |
calc(var(--animate-duration) * 2) |
| (no class) | 1s |
var(--animate-duration) |
animate__fast |
800ms |
calc(var(--animate-duration) * 0.8) |
animate__faster |
500ms |
calc(var(--animate-duration) / 2) |
Note
A few animations override duration internally:
hingeis2s(slow fall feels natural),bounceInandbounceOutare0.75s,flipOutXandflipOutYare0.75s, andheartBeatis1.3s. Speed utility classes still apply on top viacalc(), so the ratios are preserved when you change the global--animate-duration.
5.3 Repeat classes
<div class="animate__animated animate__bounce animate__repeat-2">Bounces twice</div>
| Class | Default count | Formula |
|---|---|---|
animate__repeat-1 |
1 |
var(--animate-repeat) |
animate__repeat-2 |
2 |
calc(var(--animate-repeat) * 2) |
animate__repeat-3 |
3 |
calc(var(--animate-repeat) * 3) |
animate__infinite |
infinite |
(fixed — ignores --animate-repeat) |
Important
animate__infiniteis not derived from--animate-repeat. Setting--animate-repeat: 10does nothing to it. Use it only when you truly want to loop forever.
6. Animation Catalog
The library ships 97 animations in 16 categories. Every class name below is the unprefixed animation name — remember to prepend animate__ in your HTML (for example animate__fadeInLeft).
Tip
All names use camelCase and encode direction suffixes:
-Down,-Up,-Left,-Right,-TopLeft,-TopRight,-BottomLeft,-BottomRight,-Big(longer travel distance),X/Y(axis). Once you knowfadeInLeftexists, you can guessfadeInRight,fadeInUp, andfadeInDownBig.
6.1 Attention seekers (12)
Small in-place animations to draw the eye without moving or hiding the element. Perfect for a "look here" nudge on a button or badge.
| Class | Feel |
|---|---|
bounce |
Quick vertical hop with a squash on landing |
flash |
Blinks opacity 1 -> 0 -> 1 -> 0 -> 1 |
pulse |
Soft scale from 1.0 to 1.05 to 1.0 |
rubberBand |
Stretches horizontally then vertically — cartoon stretch |
shakeX |
Side-to-side (horizontal) shake |
shakeY |
Up-and-down (vertical) shake |
headShake |
Short "no" head-shake — shorter travel than shakeX |
swing |
Pendulum from the top of the element |
tada |
Scale-down, wiggle, scale-up (celebration) |
wobble |
Translated sway with slight rotation |
jello |
Skew-based jiggle |
heartBeat |
Two quick scale pulses (duration internally 1.3x) |
Note
The v3
shake(a unified horizontal shake class) was split intoshakeX/shakeYin v4 and the originalshakeis not included in the builtanimate.min.css. If you need it by name, useanimate.compat.css(which ships the original v3 class names).
6.2 Back entrances (4)
Element arrives from off-screen, scaled down, and settles in place.
| Class | From |
|---|---|
backInDown |
Top |
backInUp |
Bottom |
backInLeft |
Left |
backInRight |
Right |
6.3 Back exits (4)
Element pulls back and leaves off-screen.
| Class | To |
|---|---|
backOutDown |
Bottom |
backOutUp |
Top |
backOutLeft |
Left |
backOutRight |
Right |
6.4 Bouncing entrances (5)
Spring-in with a settle-overshoot. Internal duration is 0.75s (shorter than the 1s baseline) so the bounce feels snappy.
| Class | From |
|---|---|
bounceIn |
Scale in (center) |
bounceInDown |
Top |
bounceInUp |
Bottom |
bounceInLeft |
Left |
bounceInRight |
Right |
6.5 Bouncing exits (5)
Spring-out, internal 0.75s duration.
| Class | To |
|---|---|
bounceOut |
Scale out (center) |
bounceOutDown |
Bottom |
bounceOutUp |
Top |
bounceOutLeft |
Left |
bounceOutRight |
Right |
6.6 Fading entrances (13)
The most commonly used category — cheap, unobtrusive, and reliable on any device.
| Class | From |
|---|---|
fadeIn |
In-place (opacity only) |
fadeInDown |
Top (short travel) |
fadeInUp |
Bottom (short travel) |
fadeInLeft |
Left (short travel) |
fadeInRight |
Right (short travel) |
fadeInDownBig |
Top (full viewport) |
fadeInUpBig |
Bottom (full viewport) |
fadeInLeftBig |
Left (full viewport) |
fadeInRightBig |
Right (full viewport) |
fadeInTopLeft |
Diagonal from top-left |
fadeInTopRight |
Diagonal from top-right |
fadeInBottomLeft |
Diagonal from bottom-left |
fadeInBottomRight |
Diagonal from bottom-right |
6.7 Fading exits (13)
Mirror of fading entrances — every fadeIn* has a matching fadeOut*.
| Class | To |
|---|---|
fadeOut |
In-place (opacity only) |
fadeOutDown |
Bottom (short travel) |
fadeOutUp |
Top (short travel) |
fadeOutLeft |
Left (short travel) |
fadeOutRight |
Right (short travel) |
fadeOutDownBig |
Bottom (full viewport) |
fadeOutUpBig |
Top (full viewport) |
fadeOutLeftBig |
Left (full viewport) |
fadeOutRightBig |
Right (full viewport) |
fadeOutTopLeft |
Diagonal top-left |
fadeOutTopRight |
Diagonal top-right |
fadeOutBottomLeft |
Diagonal bottom-left |
fadeOutBottomRight |
Diagonal bottom-right |
6.8 Flippers (5)
3D flip effects. Exits use internal 0.75s duration. All flipper animations set backface-visibility: visible !important.
| Class | Axis / style |
|---|---|
flip |
Full horizontal barrel roll using perspective(400px) + translate3d(0, 0, 150px) mid-flight |
flipInX |
Fold in around horizontal axis |
flipInY |
Fold in around vertical axis |
flipOutX |
Fold out around horizontal axis (0.75s) |
flipOutY |
Fold out around vertical axis (0.75s) |
Warning
The
flipanimation usesperspective(400px)plus atranslate3d(0, 0, 150px)mid-flight. If the element's parent hasoverflow: hidden, the mid-flip frame may clip. Test on your actual layout.
6.9 Lightspeed (4)
Skew-and-slide effect, feels cinematic.
| Class | Direction |
|---|---|
lightSpeedInRight |
In, from right |
lightSpeedInLeft |
In, from left |
lightSpeedOutRight |
Out, to right |
lightSpeedOutLeft |
Out, to left |
6.10 Rotating entrances (5)
Rotational entrance, pivoting around one corner.
| Class | Pivot |
|---|---|
rotateIn |
Center |
rotateInDownLeft |
Down, from left |
rotateInDownRight |
Down, from right |
rotateInUpLeft |
Up, from left |
rotateInUpRight |
Up, from right |
6.11 Rotating exits (5)
Mirror of rotating entrances.
| Class | Pivot |
|---|---|
rotateOut |
Center |
rotateOutDownLeft |
Down, to left |
rotateOutDownRight |
Down, to right |
rotateOutUpLeft |
Up, to left |
rotateOutUpRight |
Up, to right |
6.12 Sliding entrances (4)
Linear slide from the named side.
| Class | From |
|---|---|
slideInDown |
Top |
slideInUp |
Bottom |
slideInLeft |
Left |
slideInRight |
Right |
6.13 Sliding exits (4)
Linear slide to the named side.
| Class | To |
|---|---|
slideOutDown |
Bottom |
slideOutUp |
Top |
slideOutLeft |
Left |
slideOutRight |
Right |
6.14 Zooming entrances (5)
Scale-up entrance, optionally paired with a travel direction.
| Class | From |
|---|---|
zoomIn |
Center |
zoomInDown |
Top |
zoomInUp |
Bottom |
zoomInLeft |
Left |
zoomInRight |
Right |
6.15 Zooming exits (5)
Scale-down exit.
| Class | To |
|---|---|
zoomOut |
Center |
zoomOutDown |
Bottom |
zoomOutUp |
Top |
zoomOutLeft |
Left |
zoomOutRight |
Right |
6.16 Specials (4)
Standalone character effects that do not fit the entrance/exit taxonomy.
| Class | Feel |
|---|---|
hinge |
A hinged-in-the-corner fall, like a sign coming off the wall. Internal duration 2s — a hinge at 1s feels wrong. |
jackInTheBox |
Scales up and wiggle-rotates from the bottom center. |
rollIn |
Rolls in from the left, rotating -120deg. |
rollOut |
Rolls out to the right, rotating 120deg. |
Note
The
hingeanimation ends with the element translated+700pxvertically and opacity0. If the parent hasoverflow: hidden, the fall will clip; otherwise the element covers part of your layout for the duration. Plan the surrounding container accordingly.
7. Common Tasks
7.1 Stagger a list of items
There is no built-in stagger utility, but the animate__delay-* classes get you most of the way. Assign increasing delays:
<ul>
<li class="animate__animated animate__fadeInUp">First</li>
<li class="animate__animated animate__fadeInUp animate__delay-1s">Second</li>
<li class="animate__animated animate__fadeInUp animate__delay-2s">Third</li>
</ul>
For a tighter rhythm, override --animate-delay:
li:nth-child(1) { --animate-delay: 100ms; }
li:nth-child(2) { --animate-delay: 200ms; }
li:nth-child(3) { --animate-delay: 300ms; }
<li class="animate__animated animate__fadeInUp animate__delay-1s">...</li>
7.2 Animate only when an element scrolls into view
Animate.css ships no scroll logic. Pair it with an IntersectionObserver:
const io = new IntersectionObserver((entries) => {
entries.forEach((entry) => {
if (entry.isIntersecting) {
entry.target.classList.add('animate__animated', 'animate__fadeInUp');
io.unobserve(entry.target);
}
});
});
document.querySelectorAll('.reveal').forEach((el) => io.observe(el));
<section class="reveal">Appears when scrolled to.</section>
7.3 Replay an animation on demand
You cannot replay by re-adding the same class — the browser considers the animation unchanged and does nothing. Two workable patterns:
Pattern A — remove then re-add next frame:
el.classList.remove('animate__animated', 'animate__bounce');
// Force reflow so the browser sees "animation removed, now added again"
void el.offsetWidth;
el.classList.add('animate__animated', 'animate__bounce');
Pattern B — use the animateCSS helper (section 4.4):
await animateCSS('#thing', 'bounce');
await animateCSS('#thing', 'bounce'); // plays cleanly the second time
7.4 Page-transition pattern (SPA)
async function goTo(url) {
await animateCSS('main', 'fadeOutLeft');
const html = await fetch(url).then((r) => r.text());
document.querySelector('main').innerHTML = extractMain(html);
await animateCSS('main', 'fadeInRight');
}
7.5 Interval between repeats
Pure CSS cannot insert a gap between animation iterations. Drop animate__infinite / animate__repeat-* and re-trigger on a timer instead:
function pulseForever(el) {
setInterval(() => {
el.classList.remove('animate__pulse');
void el.offsetWidth;
el.classList.add('animate__animated', 'animate__pulse');
}, 2000);
}
7.6 Framework integration snippets
React
import 'animate.css';
function Alert({ message }) {
return (
<div className="animate__animated animate__fadeInDown">
{message}
</div>
);
}
Vue
<template>
<div class="animate__animated animate__fadeInDown">{{ message }}</div>
</template>
<script setup>
import 'animate.css';
defineProps({ message: String });
</script>
Svelte
<script>
import 'animate.css';
export let message = '';
</script>
<div class="animate__animated animate__fadeInDown">{message}</div>
Angular
In angular.json add the CSS to the styles array:
"styles": [
"src/styles.css",
"node_modules/animate.css/animate.min.css"
]
Then use classes in any template:
<div class="animate__animated animate__fadeInDown">Hello</div>
Tip
For any framework, the pattern is the same: import the CSS once globally, then apply classes. No framework-specific adapter is needed.
7.7 Migrate from v3 to v4
v3.x and earlier used unprefixed class names (.animated, .bounce, .fadeIn). v4 added an animate__ prefix to avoid collisions — this is the only breaking change.
Option A — use the compat build (fastest):
Replace your stylesheet import with the compat file. Your existing HTML keeps working unchanged.
<!-- v4 with v3 class names -->
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/animate.css/4.1.1/animate.compat.css" />
Option B — adopt the animate__ prefix:
Do a find-and-replace across your codebase: animated becomes animate__animated, bounce becomes animate__bounce, and so on.
Caution
The compat build may be removed in a future major version. Treat it as a migration aid, not a long-term solution. Also note that the v3
shakeclass was split intoshakeX/shakeYin v4 — if you usedshake, switch toshakeXor stay on the compat build.
8. JavaScript Integration
Animate.css is pure CSS — the library itself exposes no JavaScript API. Integration is done through the standard Element / Animation DOM APIs.
8.1 Listening for the end of an animation
el.addEventListener('animationend', (event) => {
console.log(event.animationName); // e.g. "bounceOutLeft"
}, { once: true });
- Use
{ once: true }to auto-remove the listener. - Call
event.stopPropagation()if a parent also has animations and you do not want itsanimationendto bubble up.
8.2 Animation events
| Event | When |
|---|---|
animationstart |
Fires at the start of each animation |
animationiteration |
Fires between iterations when animation-iteration-count > 1 |
animationend |
Fires when the animation completes (or is cancelled by removing the class) |
animationcancel |
Fires when the animation is interrupted before completing |
8.3 Reading and writing the custom properties from JS
// Read
const duration = getComputedStyle(el).getPropertyValue('--animate-duration');
// Write (this element only)
el.style.setProperty('--animate-duration', '0.6s');
// Write (global)
document.documentElement.style.setProperty('--animate-duration', '2s');
Note
Custom property overrides set via JavaScript follow the CSS cascade — an inline
style.setProperty()has the highest specificity and overrides any stylesheet-level custom property value.
9. Best Practices
9.1 Animations should mean something
Use entrance / exit animations to communicate a state change (new element appeared, element is leaving). Use attention-seekers (bounce, pulse, flash) to point at a specific thing the user needs to notice. Avoid animating purely for decoration.
9.2 Animate small, focused elements
Animating huge hero sections, full-page takeovers, or entire layouts is rarely performant and usually hurts UX. Animate the thing that changed, not the whole screen.
9.3 Do not animate <html> or <body>
There have been real browser bugs tied to animating root elements, and the user experience of a bouncing page is poor. If you want a page-level effect, wrap your content in a <main> and animate that.
9.4 Limit infinite animations
animate__infinite attracts attention forever, which quickly becomes attention-fatigue. Use it only for UI states where "something is ongoing" is the actual message.
9.5 Mind the final state
Every animation has animation-fill-mode: both — the element holds the end state after the animation finishes. For exit animations, the element remains in its "faded out" state — still taking up layout space but invisible.
If you want an exited element to actually be removed:
el.addEventListener('animationend', () => el.remove(), { once: true });
9.6 Never disable prefers-reduced-motion
The library automatically shortens animations to 1ms when the OS has "reduce motion" enabled. Do not override this. Users who enable reduced-motion typically do so because of a medical condition (vestibular disorders, seizure sensitivity).
Warning
Overriding the
prefers-reduced-motionbehavior to force animations is an accessibility violation. If an animation is load-bearing for UX (e.g., a page transition), make sure the non-animated state still communicates the change.
9.7 Inline elements do not animate
CSS animations on display: inline elements are undefined by spec — some browsers animate, some do not. If you want to animate a <span>, promote it to inline-block:
.badge { display: inline-block; }
Block-level elements, inline-block, flex / grid items, and table cells all animate reliably.
10. Accessibility
10.1 prefers-reduced-motion is built in
Animate.css honors the prefers-reduced-motion: reduce media query out of the box. When the user has requested reduced motion, all .animate__animated elements play at 1ms — effectively instantaneous — and iteration count is forced to 1.
The rule is applied at the @media print, (prefers-reduced-motion: reduce) level, so printed pages also skip animations.
10.2 What this means in practice
| User setting | What happens |
|---|---|
| Motion allowed | Animations play normally |
| Reduced motion | Animations complete in 1ms. Exit animations still leave the element invisible (opacity: 0). |
| Same as reduced motion — animations are effectively disabled |
Tip
Test your site with reduced motion enabled. On macOS: System Settings -> Accessibility -> Display -> Reduce motion. On Windows: Settings -> Accessibility -> Visual effects -> Animation effects (off). On iOS: Settings -> Accessibility -> Motion -> Reduce Motion.
11. Troubleshooting
Error: The animation does nothing
Cause: Missing animate__animated. The animation classes only set animation-name — they need animate__animated to supply duration and fill-mode.
Solution: Add both classes: class="animate__animated animate__fadeIn".
Prevention: Treat the two-class pattern as a hard rule.
Error: The element disappears after an exit animation
Cause: Working as designed. animation-fill-mode: both holds the final keyframe state, which for exit animations is opacity: 0 plus an off-screen transform.
Solution: Remove the element on animationend, or remove the classes to reset visibility:
el.addEventListener('animationend', () => el.remove(), { once: true });
Error: The animation only plays once and I cannot replay it
Cause: The browser does not restart an animation when you re-add the same class — it sees no change.
Solution: Force a reflow between remove and add:
el.classList.remove('animate__animated', 'animate__bounce');
void el.offsetWidth;
el.classList.add('animate__animated', 'animate__bounce');
Error: Class names like bounce or fadeIn do not work
Cause: You are on v4+, which requires the animate__ prefix.
Solution: Either prefix every class (animate__bounce, animate__fadeIn) or switch the stylesheet you import from animate.min.css to animate.compat.css (compat ships the v3 unprefixed names).
Error: Animation causes horizontal / vertical scrollbars
Cause: Entrance and exit animations translate elements off-screen. Those off-screen frames are valid page content and the browser extends the scrollable area.
Solution: Add overflow: hidden to the nearest container that owns the animation:
.animation-wrapper { overflow: hidden; }
Warning
Do not put
overflow: hiddenon<body>unless you mean it — it prevents page scrolling.
Error: animate__shake does not work
Cause: In v4, shake was split into shakeX and shakeY. The original shake class is not included in animate.min.css.
Solution: Use animate__shakeX or animate__shakeY. If you must keep the old name, use animate.compat.css.
Error: animationend fires multiple times
Cause: The event bubbles up from every animated descendant. If you listen on a parent, you will see one event per child.
Solution: Call event.stopPropagation(), or check event.target === el, or attach the listener to the animated element itself.
Error: The flip animation only animates partially
Cause: flip briefly translates the element +150px on the Z axis using perspective(400px). A transform-style: flat on an ancestor or overflow: hidden on a small container can clip or flatten it.
Solution: Make sure you are using both classes (animate__animated animate__flip), and check that the parent does not clip 3D transforms.
Error: animate__infinite ignores my --animate-repeat override
Cause: Intentional. animate__infinite sets animation-iteration-count: infinite as a literal value; it does not reference the custom property.
Solution: Use animate__repeat-1, animate__repeat-2, or animate__repeat-3 and override --animate-repeat. For truly infinite animation, the only option is animate__infinite.
Error: The animation works in Chrome but not in Safari / Firefox
Cause: Almost always the prefers-reduced-motion setting. Safari and Firefox respect the OS setting; if you have "Reduce Motion" enabled system-wide, animations complete in 1ms.
Solution: Check your OS motion settings. The behavior is correct and by design.
Error: Bundler can't find animate.css when I import 'animate.css'
Cause: Some bundlers need an explicit file extension or require the file inside the package.
Solution: Try import 'animate.css/animate.min.css'; or reference the path from node_modules/animate.css/animate.min.css.
Error: The hinge animation falls on top of other content
Cause: By design — hinge ends at translate3d(0, 700px, 0) with opacity: 0, but the layout still reserves the element's original box. During the 2s animation the falling element may cover whatever is beneath it.
Solution: Wrap the hinging element in an absolutely positioned container, or remove the element on animationend.
Error: Nothing animates inside Shadow DOM
Cause: Animate.css is applied as a global stylesheet. Shadow DOM encapsulation isolates styles.
Solution: Import the stylesheet inside the shadow root, or adopt a CSSStyleSheet with adoptedStyleSheets.
Error: My content clipped during animate__flipInX
Cause: flipInX uses perspective(400px) rotation from 90deg. Parent containers with overflow: hidden or no room for the rotated bounding box can clip the element mid-rotation.
Solution: Give the container enough space (or temporarily remove overflow: hidden during the animation), or pick a gentler animation (fadeIn, slideInDown).
Tip
For a loading-on-page-load issue (#1856), ensure the
<link>for animate.css is in<head>(blocking render is fine — the file is small), not in<body>. This prevents the element from rendering before the animation stylesheet loads.
12. FAQ
Q: Does animate.css require JavaScript?
No. Adding the classes to an element in HTML is enough. JavaScript is only needed if you want to trigger animations on events, remove them afterwards, or chain them.
Q: Does it work with Tailwind / Bootstrap / Bulma / Foundation?
Yes. The animate__ prefix was introduced in v4 specifically to avoid class-name collisions. If you are still on v3 (or using animate.compat.css) and you see conflicts, switch to v4's prefixed build.
Q: How do I change only one animation's duration without affecting others?
Scope the override:
.my-element.animate__bounce { --animate-duration: 500ms; }
Or inline:
<div class="animate__animated animate__bounce" style="--animate-duration: 500ms;">
Q: Can I use animate.css with server-side rendering (Next.js, Nuxt, SvelteKit)?
Yes. The stylesheet is static CSS — it loads the same way your other global styles do. No hydration concerns.
Q: Is there a way to animate route transitions (SPA)?
There is no built-in routing integration, but animateCSS from section 4.4 returns a Promise that resolves on animationend. Use it to sequence "fade out -> change route -> fade in" (see section 7.4).
Q: Why does bounceIn finish faster than fadeIn?
bounceIn, bounceOut, flipOutX, and flipOutY set a custom animation-duration: calc(var(--animate-duration) * 0.75) on their class — they are deliberately 25% shorter than the default 1s so the spring feels springy.
Q: What is the difference between fadeInLeft and fadeInLeftBig?
The Big variants translate across the full viewport width (or height). Non-Big variants only translate 100% of the element itself.
Q: Can I cancel an animation partway through?
Yes — remove the animate__animated class (or the animation class). The element snaps to its current CSS state immediately. Use the animationcancel event to detect this programmatically.
Q: Does animate.css support right-to-left (RTL) languages?
Directional animations (*Left, *Right) refer to physical directions, not logical ones. If your UI is RTL, fadeInLeft still comes from the geometric left. If you need mirror behavior, swap the classes in your RTL stylesheet or use fadeIn (non-directional) where possible.
Q: How big is the production build?
animate.min.css is roughly 60 KB uncompressed and approximately 9 KB gzipped. If that is too heavy, build a custom version with only the animations you use — see the Admin Guide's Custom Builds section.
Q: Can I chain two animations on the same element?
Not simultaneously — the second class wins whichever sets animation-name last. Run them sequentially by listening for animationend and swapping classes:
el.classList.add('animate__animated', 'animate__fadeIn');
el.addEventListener('animationend', () => {
el.classList.remove('animate__fadeIn');
el.classList.add('animate__pulse', 'animate__infinite');
}, { once: true });
Q: Can I use it in a static email?
Most email clients strip <style> blocks and ignore @keyframes. Animations will not play in Gmail, Outlook, or any mobile mail client. The animated elements will appear in their final state.
Q: Is there an Intellisense / autocomplete plugin for my editor?
There is community interest (see issue #1855) but no official plugin. Most modern editors autocomplete from the loaded CSS once the stylesheet is imported into your project.
13. Reference Quick Card
Two-class recipe
class="animate__animated animate__<NAME>"
Utility classes (stackable)
Speed : animate__slower animate__slow animate__fast animate__faster
Delay : animate__delay-1s ... animate__delay-5s
Repeat : animate__repeat-1 animate__repeat-2 animate__repeat-3 animate__infinite
Custom properties (overridable)
--animate-duration default 1s
--animate-delay default 1s
--animate-repeat default 1
JS events on animated element
animationstart animationiteration animationend animationcancel
Accessibility
Respects @media (prefers-reduced-motion: reduce) — animations become 1ms.
Never disable this.
Totals
97 animations in 16 categories.
3 custom properties.
5 speed utilities, 5 delay utilities, 4 repeat utilities.