# Animate.css User Guide

> _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>`:

```html
<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):

```html
<h1 class="animate__animated animate__bounce">An animated element</h1>
```

Reload the page. The `<h1>` bounces. That's the entire API.

```text
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 example `animate__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)"

    ```html
    <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"

    ```bash
    npm install animate.css --save
    ```

    ```text
    added 1 package, and audited N packages in Xs
    ```

=== "Yarn"

    ```bash
    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"

    ```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"

    ```js
    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), and `animate.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:

```html
<p class="animate__animated animate__fadeIn">If you see this fade in, it works.</p>
```

```text
Expected result: the paragraph fades in over 1 second on page load.
```

> [!WARNING]
> The `prefers-reduced-motion` media 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.

```css
/* Global slow-motion */
:root { --animate-duration: 2s; }

/* Marketing section: snappy */
.marketing-hero { --animate-duration: 400ms; }
```

From JavaScript:

```js
// 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 `:root` when possible. A global `--animate-duration: 200ms` makes 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`.

```html
<!-- 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:

```html
<div class="animate__animated animate__<NAME>">...</div>
```

- `animate__animated` — wires up the shared machinery: `animation-duration: var(--animate-duration)` and `animation-fill-mode: both`. Without it, nothing animates.
- `animate__<NAME>` — picks which keyframe runs. For example, `animate__fadeInLeft`, `animate__bounceOutUp`, or `animate__rubberBand`.

> [!NOTE]
> Because `animation-fill-mode: both` is the default, an element animated with `animate__fadeOut` stays **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.

```html
<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.

```html
<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.

```js
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:

```js
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`:

```js
function dismiss(el) {
  el.classList.add('animate__animated', 'animate__fadeOutUp');
  el.addEventListener('animationend', () => el.remove(), { once: true });
}
```

### 4.6 Loop an animation

```html
<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:

```css
.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__bounce` sets `transform-origin: center bottom`, `.animate__flipInX` sets `backface-visibility: visible !important`, `.animate__hinge` sets a 2x duration). If you reference only the `@keyframes` and 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`).

```html
<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:

```css
/* 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.

```html
<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: `hinge` is `2s` (slow fall feels natural), `bounceIn` and `bounceOut` are `0.75s`, `flipOutX` and `flipOutY` are `0.75s`, and `heartBeat` is `1.3s`. Speed utility classes still apply on top via `calc()`, so the ratios are preserved when you change the global `--animate-duration`.

### 5.3 Repeat classes

```html
<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__infinite` is **not** derived from `--animate-repeat`. Setting `--animate-repeat: 10` does 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 know `fadeInLeft` exists, you can guess `fadeInRight`, `fadeInUp`, and `fadeInDownBig`.

### 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 into `shakeX` / `shakeY` in v4 and the original `shake` is **not** included in the built `animate.min.css`. If you need it by name, use `animate.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 `flip` animation uses `perspective(400px)` plus a `translate3d(0, 0, 150px)` mid-flight. If the element's parent has `overflow: 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 `hinge` animation ends with the element translated `+700px` vertically and opacity `0`. If the parent has `overflow: 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:

```html
<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`:

```css
li:nth-child(1) { --animate-delay: 100ms; }
li:nth-child(2) { --animate-delay: 200ms; }
li:nth-child(3) { --animate-delay: 300ms; }
```

```html
<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`:

```js
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));
```

```html
<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:**

```js
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):**

```js
await animateCSS('#thing', 'bounce');
await animateCSS('#thing', 'bounce'); // plays cleanly the second time
```

### 7.4 Page-transition pattern (SPA)

```js
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:

```js
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"

    ```jsx
    import 'animate.css';

    function Alert({ message }) {
      return (
        <div className="animate__animated animate__fadeInDown">
          {message}
        </div>
      );
    }
    ```

=== "Vue"

    ```vue
    <template>
      <div class="animate__animated animate__fadeInDown">{{ message }}</div>
    </template>

    <script setup>
    import 'animate.css';
    defineProps({ message: String });
    </script>
    ```

=== "Svelte"

    ```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:

    ```json
    "styles": [
      "src/styles.css",
      "node_modules/animate.css/animate.min.css"
    ]
    ```

    Then use classes in any template:

    ```html
    <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.

```html
<!-- 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 `shake` class was split into `shakeX` / `shakeY` in v4 — if you used `shake`, switch to `shakeX` or 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

```js
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 its `animationend` to 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

```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:

```js
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-motion` behavior 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`:

```css
.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`). |
| Print | 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:

```js
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:

```js
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:

```css
.animation-wrapper { overflow: hidden; }
```

> [!WARNING]
> Do not put `overflow: hidden` on `<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:

```css
.my-element.animate__bounce { --animate-duration: 500ms; }
```

Or inline:

```html
<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:

```js
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](https://github.com/animate-css/animate.css/issues/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

```text
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.
```

---

_Generated May 2026 from animate-css/animate.css at 3f8ab23. AI-generated documentation. The library is checked by sampling (three repositories so far); this guide was not individually reviewed. Verify against the project before relying on it. Unofficial; not affiliated with or reviewed by the project._
