Skip to main content
{/}codefunc
CSS & DesignAugust 1, 2026 · 6 min read

CSS Transitions, Keyframes, and Units: Motion That Stays Maintainable

Choose transitions vs @keyframes, pick units that scale, and build reusable motion snippets — with performance and accessibility rules that keep UI snappy.

By codefunc

csstransitionskeyframesunitsanimationrem

Motion clarifies state: a panel opens, a toast arrives, a button acknowledges a press. Uncontrolled motion distracts and costs frames. CSS gives you two primary tools — transitions (A → B when a property changes) and keyframes (multi-step timelines) — plus a unit system that decides whether sizes scale with user font settings. This guide ties those pieces together so motion tokens stay consistent across a codebase.

Bottom line: Use transitions for simple state changes; use @keyframes for sequenced or looping motion. Animate transform and opacity first. Size with rem for UI chrome. Prototype with a CSS Transition Builder, CSS Keyframes Generator, and CSS Unit Converter; assemble fuller clips in a CSS Animation Builder.

Transitions vs keyframes vs animation builder mindset

Tool When to use
transition Hover, focus, open/close, color/opacity toggles driven by a class or state
@keyframes + animation Entrances with several stages, loaders, attention pulses, continuous loops
JS animation libraries Gesture-driven spring physics, orchestrating many elements — out of scope here

If a single property interpolates between two values when a class is added, start with a transition. If you need "0% → 40% → 100%" choreography, write keyframes.

Transitions: the dependable default

.button {
  transform: translateY(0);
  transition:
    transform 160ms var(--ease-out),
    background-color 160ms var(--ease-out);
}

.button:hover {
  transform: translateY(-1px);
}

Properties worth transitioning

Prefer Avoid animating (often)
transform top / left (layout)
opacity height on large subtrees (use transforms or discrete steps)
background-color (small areas) Huge box-shadow spreads every frame
clip-path (carefully) Animating expensive filters on scroll

Generate timing and easing candidates in the CSS Transition Builder, then promote winners to CSS variables:

:root {
  --duration-fast: 120ms;
  --duration-base: 200ms;
  --ease-out: cubic-bezier(0.22, 1, 0.36, 1);
}

Transition gotchas

Gotcha Detail
transition: all Surprises you when width or color join the party
Entering from display: none No transition — toggle visibility/opacity/transform instead
Inconsistent lists Transitioning 3 properties out but 1 back feels broken — keep lists matched
Zero duration in reduced motion Honor prefers-reduced-motion (below)

Keyframes: sequences and loops

@keyframes toast-in {
  0% {
    opacity: 0;
    transform: translateY(8px);
  }
  100% {
    opacity: 1;
    transform: translateY(0);
  }
}

.toast {
  animation: toast-in 220ms var(--ease-out) both;
}
Keyword Role
animation-name Which @keyframes
duration How long one cycle lasts
timing-function Easing between keyframe stops
delay Stagger starts
iteration-count 1 or infinite
direction normal, alternate, …
fill-mode both / forwards keeps end state

Build step lists visually with the CSS Keyframes Generator. For multi-property clips and previews, use the CSS Animation Builder, then copy into your design tokens file.

Keyframe design rules

  1. Fewer stops — 2–3 beats read clearer than 8 micro-adjustments.
  2. Same properties at every stop you care about — missing properties interpolate from computed values in ways that surprise.
  3. Looping loaders — prefer opacity/transform orbits; avoid animating width on a progress parent that reflows children.
  4. Exit animations — plan fill-mode and class removal order so the element is not display-killed mid-flight.

Units: px, rem, em, %, and friends

Motion values often mix distances (translateY(0.5rem)) with durations (200ms). Layout units deserve the same discipline as color tokens.

Unit Meaning Typical use
px Absolute CSS pixels Hairlines, borders, icon snugness
rem Relative to root font size Component spacing, UI type, many offsets
em Relative to element font size Padding that scales with local type
% Relative to containing block Widths; transforms % refer to the element itself
vh / vw Viewport Full-bleed sections — careful on mobile toolbars
ms / s Time Durations and delays

Convert during refactors with a CSS Unit Converter (set your root size — often 16px).

Why rem helps motion and a11y

If a user raises root font size, translateY(8px) stays 8px while type grows — offsets can feel tight. translateY(0.5rem) scales with their preference. Pair that with prefers-reduced-motion so scaled motion can also shorten or disable.

@media (prefers-reduced-motion: reduce) {
  *,
  *::before,
  *::after {
    animation-duration: 0.01ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: 0.01ms !important;
  }
}

Prefer a subtler approach in design systems: swap long animations for instant state changes on the media query, without !important hammers when you can scope tokens instead.

Composing a small motion scale

Ship a short scale — not a new curve per component:

:root {
  --motion-fast: 120ms;
  --motion-base: 200ms;
  --motion-slow: 320ms;
  --ease-standard: cubic-bezier(0.2, 0, 0, 1);
  --ease-emphasized: cubic-bezier(0.2, 0, 0, 1);
  --space-nudge: 0.25rem;
}
Token Use
Fast Buttons, toggles
Base Panels, toasts
Slow Page-level reveals (rare)
Nudge translateY(var(--space-nudge))

Prototype durations and easings in the CSS Transition Builder; convert nudge distances from legacy px mocks with the CSS Unit Converter.

Performance checklist

Do Don't
Animate transform / opacity Animate layout props on scroll-linked handlers
Keep animated layers small Blur + shadow + scale on huge images simultaneously
Use will-change sparingly and temporarily Leave will-change: transform on everything forever
Pause offscreen infinite animations Run infinite spinners in hidden tabs unchecked

Infinite animations are fine for a single loader. Ten decorative loops in a dashboard are not.

End-to-end workflow

  1. Decide transition vs keyframes from the table above.
  2. Draft the interaction in CSS Transition Builder or CSS Keyframes Generator.
  3. Combine / preview richer sequences in CSS Animation Builder.
  4. Normalize distances and type-related offsets with CSS Unit Converter.
  5. Store results as CSS variables — avoid one-off magic numbers in components.
  6. Verify with keyboard focus and reduced-motion enabled.

Practical takeaway

Transitions handle state flips; keyframes handle choreography and loops. Animate transform and opacity, express offsets in rem, and keep a short motion scale in CSS variables. Prototype with the CSS Transition Builder and CSS Keyframes Generator, refine in the CSS Animation Builder, and convert legacy pixel values with the CSS Unit Converter. Honor prefers-reduced-motion so clarity never depends on animation alone.

Sources

Try it on codefunc

Related articles

3

Search tools

Find a developer tool