Skip to content
All designer tools

CSS Keyframes Editor

Author a multi-stop animation on a timeline and copy it with its reduced-motion guard already written

Loading one replaces the timeline. Two of the presets over there are not on this list — they animate things a pixel timeline cannot express, and that page says which and why.

rise-in
2 stops · 600ms
cssCSS
@keyframes rise-in {
  0% {
    transform: translate(0px, 16px) scale(0.96);
    opacity: 0;
  }
  100% {
    transform: none;
  }
}

.rise-in {
  animation: rise-in 600ms cubic-bezier(0.4, 0, 0.2, 1) both;
}

@media (prefers-reduced-motion: reduce) {
  .rise-in {
    animation-duration: 0.01ms !important;
    animation-iteration-count: 1 !important;
  }
}
htmlHTML
<div class="rise-in">…</div>

Now use it on something

Every effect, block, page and template in the catalog is styled against tokens shaped exactly like these — so they take your values rather than fighting them.

Hand these tokens to an agent

npx shadcn@latest add https://hoverlab.dev/r/hoverlab.json

Position0%

Where on the timeline this frame sits, as a percentage of the duration. Two stops close together make a fast move with a pause either side — that is how you get a snap rather than a drift.

Opacity0%

100% is fully opaque. Fading from 0 is the cheapest enter animation there is, and the one that survives reduced motion best.

Translate X0px

Horizontal offset. Transforms are composited on the GPU and never trigger layout, which is why animating this instead of `left` is the difference between 60fps and jank.

Translate Y16px

Vertical offset. A rise of 8–24px is the standard enter; more than that and the element reads as flying in rather than settling.

Scale96%

100% is untouched. Small scales read as depth; below about 90% on an enter it starts to look like a zoom rather than an arrival.

Rotate0°

Applied after the translate and before the scale, because CSS resolves a transform list right to left and reordering it silently changes the result.

Blur0px

The one property here that is not free — filter forces a repaint every frame. Effective in small doses on a single element, expensive across a list.

Used for both the @keyframes and the class. Emitted as rise-in — spaces and anything else invalid in a CSS identifier are folded to hyphens.

Duration600ms

Under about 150ms an animation reads as a jump; over about 500ms it reads as a wait. Enter and exit want the short end; attention loops can afford the long one.

Delay0ms

Held before the first frame. Staggering a list by 40–60ms per item is what makes a group arrive as a group rather than a stampede.

Applies between every pair of stops, not once across the whole animation. Want a different curve on one segment? Add a stop.

Iterations1

How many times it runs. Zero here means infinite — and an infinite animation gets the stricter reduced-motion guard, because a loop shortened to a millisecond is a loop running very fast forever.

alternate is how a pulse is written once instead of twice — you author half the movement and let the return trip come free.

The reason an enter animation snaps back at the end: none drops the animated values the instant it finishes. both is the safe default for anything that arrives and stays.

Shortcut for setting iterations to infinite. Useful while you are shaping the curve and wrong for almost anything you ship — an animation that never stops is an animation nobody can read past.

Keep this animation. This browser remembers what you last had open. A free account remembers the ones you name, on every machine you use.

More designer tools

All 36 run in the tab — no account, no upload.See all