Generate CSS animation (CSSANIMATION) code quickly and copy or download the result for your project.
@keyframes pulse {
0%, 100% { transform: scale(1); }
50% { transform: scale(1.1); }
}
.target {
animation-name: pulse;
animation-duration: 2s;
animation-delay: 0s;
animation-iteration-count: infinite;
animation-timing-function: ease-in-out;
animation-direction: normal;
animation-fill-mode: none;
}fade, slide (left/right/up/down), bounce, zoom, shake, pulse, rotate, or a blank @keyframes skeleton to author from scratch. Each preset gives you a working from / to (or 0–100%) keyframe block you can edit inline.400ms or 1s), an optional delay, then pick a timing function — linear, ease, ease-in, ease-out, ease-in-out, steps(n), or a custom cubic-bezier(x1, y1, x2, y2). Set iteration count (1, a number, or infinite) and direction (normal, reverse, alternate, alternate-reverse). animation-fill-mode (none / forwards / backwards / both) and animation-play-state (running / paused) round out the shorthand.transform, opacity, background, or color until the motion looks right.@keyframes and the animation Shorthand: Click "Copy CSS" to grab both the @keyframes block and the animation: shorthand line. Paste straight into your stylesheet — no build step, no JavaScript runtime, no dependencies.CSS animations are declarative keyframe sequences that run on the browser's compositor thread when authored well. Compared with hand-writing @keyframes and remembering the order of the animation shorthand (name | duration | timing-function | delay | iteration-count | direction | fill-mode | play-state), a generator lets you tune motion visually and copy production-ready CSS. Typical reasons people reach for one:
fade or slide-up on hero text or CTA buttons, with ease-out timing, is the bread and butter of landing pages. Material Design 3's "standard" easing token is cubic-bezier(0.2, 0, 0, 1) (the legacy MD2 value was cubic-bezier(0.4, 0, 0.2, 1)); both are still common defaults in component libraries.pulse (opacity 1 → 0.4 → 1) or shimmer (background-position translate) at 1.2–2s, ease-in-out, paired with animation-iteration-count: infinite, gives a polished placeholder while data loads.shake on an invalid input (4–6 keyframes, 300–400ms, ease-in-out) is more accessible than a colour change alone.slide + fade on entry with animation-fill-mode: forwards keeps the toast visible at its final position after the animation ends.transform: translateX(-100%) over 10–30s, linear, iteration-count: infinite — purely compositor-driven, costs almost nothing to render.bounce or pulse on a "Got it" button draws the eye without the heavy-handedness of an autoplaying video.Everything generated here is pure CSS — no library, no JavaScript, no bundler change. Drop it into a Tailwind project, a vanilla HTML page, a React component, or a WordPress theme.
These three technologies do related work but solve different problems. Pick the one that matches your trigger and complexity needs.
| Property | CSS Transition | CSS Animation | Web Animations API (WAAPI) |
|---|---|---|---|
| What it animates | Between two computed states (e.g., :hover, class toggle) |
A sequence of keyframes (@keyframes) |
Keyframes constructed in JavaScript |
| Trigger | State change (hover, focus, class swap) | Auto on element render, or class-driven | element.animate(keyframes, options) JS call |
| Multiple steps | No — start state to end state only | Yes — any number of keyframe percentages | Yes — keyframes array in JS |
| Pause / seek / reverse | No (you can't pause a transition) | animation-play-state: paused for pause only |
Full .pause() / .play() / .reverse() / .currentTime |
| Default timing function | ease |
ease |
linear |
| Returns a handle | No | No (events only) | Animation object with .finished Promise |
| Best for | Hover effects, class transitions, theme changes | Loading spinners, page-load reveals, looped motion | Sequenced animations, chained promises, interactive control |
| Baseline browser support | Baseline widely available since September 2015 | Baseline widely available since September 2015 | Available in Chrome, Firefox, Edge, Safari (Animation interface widely supported since 2020) |
Rule of thumb: if a class flip can express it, use transition. If it needs more than two steps or runs on load, use animation. If you need to coordinate, await completion, or scrub mid-animation, use WAAPI.
| Function | Cubic-Bezier Equivalent | Feel | When to use |
|---|---|---|---|
linear |
cubic-bezier(0, 0, 1, 1) |
Constant speed — robotic | Marquee/ticker, progress bars, loops where any "ease" feels wrong |
ease (default) |
cubic-bezier(0.25, 0.1, 0.25, 1) |
Slow start, fast middle, slow end | Generic motion when you haven't picked anything more specific |
ease-in |
cubic-bezier(0.42, 0, 1, 1) |
Slow start, fast end | Element leaving the screen — accelerating away |
ease-out |
cubic-bezier(0, 0, 0.58, 1) |
Fast start, slow end | Element entering the screen — landing softly. Most common UI default. |
ease-in-out |
cubic-bezier(0.42, 0, 0.58, 1) |
Slow start and end, fast middle | Elements that move and stay on screen (Material's "standard" use case) |
cubic-bezier(0.4, 0, 0.2, 1) |
— | Material 2 "standard" curve | Generic Material-flavoured motion (the legacy default) |
cubic-bezier(0.2, 0, 0, 1) |
— | Material 3 "standard" emphasized curve | Newer Material 3 motion tokens |
steps(n, end) |
— | Discrete jumps (no interpolation) | Sprite sheets, typewriter effects, frame-by-frame animation |
For finer control, lock in a custom curve on cubic-bezier.com or use Chrome DevTools' Easing Editor (right-click any animation timing function in the Styles panel).
A transition interpolates between two states — the element has property X, you change it to property Y (usually via a class toggle, :hover, or :focus), and the browser fills in the frames. A transition needs a state change to fire. A CSS animation is driven by a @keyframes block and runs as soon as the element receives the animation property (or its class is added). Animations can have any number of intermediate keyframes (0%, 25%, 50%, 100%) and can loop with animation-iteration-count: infinite. Use a transition for hover/focus/class-toggle effects; use an animation for loops, multi-step motion, or anything that should auto-play on render.
cubic-bezier(0.4, 0, 0.2, 1) the Material Design "standard" ease?It was — that's the Material Design 2 "standard" easing curve, an ease-in-out variant used for elements that enter and stay on screen. Material Design 3 redefined the motion tokens; the M3 "standard" curve is now cubic-bezier(0.2, 0, 0, 1) (one of several emphasized / standard variants in the M3 spec). Both are still common in the wild — pick whichever your design system uses. For generic UI motion, ease-out (cubic-bezier(0, 0, 0.58, 1)) is the universally safe default.
Wrap motion in the prefers-reduced-motion media query and provide a near-instant fallback. The W3C technique (C39 for WCAG 2.1) recommends this pattern:
@media (prefers-reduced-motion: reduce) {
*, *::before, *::after {
animation-duration: 0.01ms !important;
animation-iteration-count: 1 !important;
transition-duration: 0.01ms !important;
}
}
This isn't optional polish — motion can trigger vestibular disorders, migraines, and seizures, and macOS, iOS, Windows, and Android all expose an OS-level "Reduce Motion" toggle that browsers honour. The MDN guidance is to disable motion that isn't essential to conveying information; for essential motion, slow it down rather than removing it.
animation-fill-mode: forwards vs backwards — what's the practical difference?forwards keeps the last keyframe's computed styles after the animation ends — useful for a fade-in that should stay visible. backwards applies the first keyframe's styles immediately when the animation is attached, including during animation-delay — useful when you want an element to start invisible (or off-screen) during the delay rather than flash in its default state, then animate in. both does both. Default none reverts the element to its un-animated styles before and after, which is rarely what you want for entrance animations.
Yes — set animation-play-state: paused to freeze it, then back to running to resume from exactly where it stopped (not from the beginning). A common pattern: start the animation paused and switch to running on hover, intersection-observer trigger, or a class toggle from JavaScript. Note this only pauses/resumes — to seek an animation to an arbitrary point or reverse it programmatically, you need the Web Animations API (element.getAnimations()[0].currentTime = ...).
Yes. The animation shorthand is comma-separated, and @keyframes blocks compose by property — as long as two animations don't target the same CSS property at the same time, they layer cleanly. Example: animation: fade-in 400ms ease-out, slide-up 400ms ease-out; runs both together. If two animations both animate transform, the later one in the list wins (the cascade rule), so combine them into a single @keyframes instead.
transform and opacity instead of width, height, top, or left?Because transform and opacity (along with filter and clip-path) can be handled on the compositor thread — the GPU moves an existing layer without re-laying-out the page or repainting pixels. Animating width, height, margin, top, or left triggers layout (reflow) and paint on every frame, which is up to an order of magnitude more expensive and stutters on low-end devices. Web.dev's high-performance animation guide explicitly recommends sticking to transform and opacity whenever possible; reach for transform: scaleX() instead of animating width, and transform: translate() instead of animating top/left. Add will-change: transform (sparingly) to hint the browser to promote the element to its own compositor layer.
Material Design 3 recommends 200–500ms for most UI motion; Apple's Human Interface Guidelines use 300ms as a typical baseline. Practical ranges: hover/focus micro-interactions 100–200ms, entrance/exit animations 200–400ms, page transitions 300–500ms, attention-grabbing loops (pulse, bounce) 1–2s. Anything above ~500ms for an interaction starts to feel sluggish; anything below ~100ms feels instantaneous and the motion may go unnoticed.
CSS animations and the @keyframes rule have been Baseline widely available since September 2015 — every current Chrome, Firefox, Safari, and Edge supports them without vendor prefixes. cubic-bezier(), steps(), animation-fill-mode, and animation-play-state are all in the same baseline. The -webkit- prefix is no longer needed for any modern target; only consider it if you're shipping to legacy Android WebView (pre-Chromium, before 2015) or KaiOS.
After exporting from the generator, run the output through CSS Minifier to strip whitespace and comments for production, or CSS Formatter if you want it pretty-printed for a stylesheet you'll edit by hand. Both run entirely in your browser. For colour stops inside background keyframes, Color Converter helps normalize between hex, RGB, HSL, and OKLCH.