What a CSS animation actually is
A CSS animation has two halves that always work together. The first is a @keyframes rule that describes what changes — a list of styling snapshots at different points along a timeline. The second is the animation shorthand applied to an element, which describes how those snapshots play: how long they take, how they accelerate, how many times they repeat, and what happens before and after. Miss either half and nothing moves.
The CSS Animation Generator builds both halves for you from a visual preview, but understanding the pieces makes the output far easier to customise. Let’s walk through them.
The @keyframes rule
A keyframes rule is a named timeline. You give it a name, then list percentage offsets from 0% (the start) to 100% (the end), each with the styles the element should have at that moment:
@keyframes slideUp {
0% { transform: translateY(40px); opacity: 0; }
100% { transform: translateY(0); opacity: 1; }
}
The browser interpolates smoothly between the offsets you define. You don’t have to list every percent — only the points where something meaningful changes. A simple fade needs just 0% and 100%; a bounce needs a few intermediate stops so the element can overshoot and settle. The keywords from and to are aliases for 0% and 100%.
The name you choose matters because the animation shorthand refers to it. It must be a valid CSS identifier: start with a letter, then letters, digits, hyphens, or underscores. The generator validates this for you and warns if the name is invalid, which is a common reason an animation silently fails to run.
The animation shorthand, field by field
Once you have keyframes, you bind them to an element. The longhand version is verbose:
.element {
animation-name: slideUp;
animation-duration: 600ms;
animation-timing-function: ease-out;
animation-delay: 0ms;
animation-iteration-count: 1;
animation-direction: normal;
animation-fill-mode: both;
}
The shorthand packs all of that into one line, in a fixed order:
.element {
animation: slideUp 600ms ease-out 0ms 1 normal both;
}
Here is what each value controls:
- name — which
@keyframesrule to play. - duration — how long one cycle takes. The generator works in milliseconds for precision.
- timing function (easing) — the acceleration curve.
easestarts and ends slowly;linearis constant;ease-in/ease-outweight the slowdown to one end. A spring-likecubic-bezier()can overshoot for a playful feel. - delay — how long to wait before starting.
- iteration count — how many times to repeat, or
infinitefor continuous motion like spinners. - direction —
normal,reverse,alternate(ping-pong), oralternate-reverse. - fill mode — what the element looks like outside the animation window (covered below).
Because the order is fixed and easy to get wrong by hand, generating the shorthand is one of the biggest time-savers. Build it visually with the CSS Animation Generator and you never have to remember whether delay comes before or after iteration count.
Easing: the difference between cheap and polished
Easing is what separates motion that feels mechanical from motion that feels designed. A linear fade looks robotic; the same fade with ease-out feels like it’s decelerating naturally to a stop. As a rule of thumb, entrances look best with ease-out (fast in, gentle landing), exits with ease-in, and looping attention-grabbers with ease-in-out. When you want personality — a button that springs, a badge that pops — reach for a back-easing cubic-bezier that overshoots slightly before settling.
Fill modes, demystified
Fill mode is the setting people most often get wrong. By default (none), an element snaps to its keyframe styles only while the animation is running, then reverts to its normal CSS. That means a fade-in element is fully visible before the animation starts and again after it ends — usually not what you want.
forwardsholds the final keyframe after the animation finishes.backwardsapplies the first keyframe during any start delay.bothdoes both, which is the safe default for entrance animations.
If your faded-in element flashes visible before animating, switching fill mode to both almost always fixes it.
Animate the right properties
Not all properties are equal. Animating transform and opacity is cheap because the browser hands the work to the GPU compositor — no layout, no repaint. Animating geometry properties like width, height, top, or margin forces the browser to recalculate layout on every frame, which causes jank on slower devices. Every preset in the CSS Animation Generator sticks to transform and opacity for exactly this reason, so the motion you copy stays smooth.
If you find yourself wanting to move or resize an element, reach for a transform instead of changing its box. Our companion CSS transform guide explains how translate, scale, rotate, and skew combine — those same functions are what your keyframes should animate between.
Putting it together
Suppose you want a card that slides up and fades in once, holding its final position. You’d define a slideUp keyframes rule, then apply animation: slideUp 500ms ease-out 0ms 1 normal both;. The both fill mode keeps it hidden during any delay and visible after it lands; the ease-out curve makes the landing feel soft; and because you animated transform and opacity, it runs at 60fps.
That’s the whole model: define the timeline, bind it with the right timing, animate compositor-friendly properties, and pick the fill mode that matches your intent. Build it once in the CSS Animation Generator, copy the output, and tune the numbers with the understanding above.
Respect user preferences
One last note: some people enable “reduce motion” in their operating system because animation makes them dizzy or distracted. Wrap non-essential animations in a media query so they’re disabled for those users:
@media (prefers-reduced-motion: reduce) {
.element { animation: none; }
}
It’s a small addition that makes your interface considerate as well as lively.