This generator produces ready-to-use CSS keyframe animations for the most common UI effects. Tune the timing, preview it live, and copy clean CSS you can drop straight into any project — no animation library or JavaScript needed.
How the generated CSS is structured
A CSS animation has two parts: a @keyframes block that describes the steps, and
an animation declaration that attaches it to an element. This tool builds both.
For each effect it writes the appropriate keyframe steps. A fade animates
opacity from 0 to 1; a slide combines opacity with a translateX or
translateY offset that returns to 0; bounce and shake use multi-stop
transform steps; spin rotates a full 360°; and pulse scales between 1 and 1.08.
It then emits an animation-* set using your duration, delay, timing function,
and iteration count, with animation-fill-mode: both so the element holds its
start and end states.
The exported class is gera-animated, for example:
.gera-animated {
animation-name: gera-fade;
animation-duration: 800ms;
animation-timing-function: ease;
animation-iteration-count: 1;
animation-fill-mode: both;
}
Effect guide and when to use each
| Effect | When to use | Recommended timing |
|---|---|---|
| Fade | Page elements entering view, toasts, tooltips | 300–500ms, ease-out |
| Slide | Drawers, sidebars, off-canvas panels | 250–400ms, ease-out |
| Bounce | Success confirmations, attention-drawing calls to action | 600–800ms, ease |
| Spin | Loading spinners, processing indicators | infinite, linear |
| Pulse | Notification badges, heartbeat loaders | infinite, ease-in-out |
| Shake | Error states, failed form validation | 400–600ms, ease |
Worked example: entrance card animation
You want a card to slide up and fade in when a page loads. Choose slide, direction up, duration 400ms, delay 100ms, timing ease-out, iteration 1:
@keyframes gera-slide-up {
from {
opacity: 0;
transform: translateY(20px);
}
to {
opacity: 1;
transform: translateY(0);
}
}
.gera-animated {
animation-name: gera-slide-up;
animation-duration: 400ms;
animation-delay: 100ms;
animation-timing-function: ease-out;
animation-iteration-count: 1;
animation-fill-mode: both;
}
Apply .gera-animated to each card. The animation-fill-mode: both keeps the card invisible during the 100ms delay, then holds the final visible state after the animation finishes.
Timing function cheat sheet
The timing function controls how the animation progresses over its duration:
- ease — starts slow, speeds up, ends slow. Good all-rounder.
- ease-out — starts fast, decelerates to a stop. Best for elements entering the screen; feels natural.
- ease-in — starts slow, accelerates. Good for elements leaving the screen.
- linear — constant speed. Correct for spinners and anything that should feel mechanical.
- cubic-bezier — custom curve. Use an overshoot value (for example
cubic-bezier(0.34, 1.56, 0.64, 1)) to add a springy bounce past the end state.
Performance: why these effects only touch transform and opacity
Every effect this generator emits animates only transform and opacity —
deliberately. Browsers can hand those two properties to the compositor thread
and animate them on the GPU without recalculating layout or repainting, so the
animation stays smooth even while the main thread is busy. Animating width,
height, top/left, or margin instead forces layout on every frame and is
the classic cause of janky entrance animations on mid-range phones. If you
customise the generated keyframes, stay inside transform/opacity
(and filter where needed) to keep the fast path.
Two related gotchas:
- Existing transforms get overwritten. The keyframes set the whole
transformproperty, so an element already positioned withtransform: translateX(-50%)will jump when the animation starts. Combine the values inside the keyframes (e.g.translate(-50%, 20px)), or animate a wrapper element instead. display: noneelements don’t animate in. Switching fromdisplay:nonetoblockand adding the class in the same frame skips the animation in some browsers. Toggle visibility first, force a reflow (or userequestAnimationFrame), then add the class — or keep the element rendered and animate opacity.
Replaying and staggering without a library
- Replay on demand: removing and re-adding the class only works if the
browser processes a style flush in between. The reliable pattern is
el.classList.remove('gera-animated'); void el.offsetWidth; el.classList.add('gera-animated');— theoffsetWidthread forces the reflow. Alternatively, listen foranimationendand reset there. - Staggered lists: give every item the same class and offset the delays
with a custom property: set
style="--i: 3"per item andanimation-delay: calc(var(--i) * 80ms)in your stylesheet. An 80–120ms step reads as a deliberate cascade; longer gaps feel sluggish. - Respect reduced motion by default: rather than opting motion out,
gate it in — declare the
animation-*properties inside@media (prefers-reduced-motion: no-preference)so users who have asked the OS for less motion simply see the element in its final state. Vestibular disorders make large slides and shakes genuinely unpleasant; fades are the safest effect to leave enabled.
Tips and notes
For entrance effects (fade, slide) keep the iteration count at 1 and use an
ease-out timing function so motion settles naturally. For loaders use spin or
pulse with infinite and linear. The cubic-bezier overshoot option adds a
springy feel to slide and bounce. Respect users who prefer reduced motion by
wrapping the class in a @media (prefers-reduced-motion: no-preference) query in
your own stylesheet.
Sources and references
- MDN — @keyframes — the at-rule the tool generates
- MDN — animation shorthand and sub-properties — duration, delay, timing-function, iteration-count, fill-mode
- MDN — prefers-reduced-motion — the accessibility media query to gate motion
- MDN — easing functions (cubic-bezier) — how the overshoot curve works
Maintained by the Gera Tools editorial team. The exported CSS is pure @keyframes + a class with no JavaScript dependency; wrap it in a reduced-motion query for accessibility. Last reviewed 2026-07-02.