Start with the feeling, not the curve
Every easing curve is a personality. Linear says 'machine'. ease-out says 'expensive'. back-out says 'confident'. elastic says 'celebration'. If you pick curves by copying presets, your page has the personality of a default theme.
The six curves that cover 90% of UI
- linear — progress bars, marquees, anything mechanical. Never for entrance motion.
- ease-out-expo (0.16 1 0.3 1) — the workhorse entrance. Fast start, long settle = 'premium'. Use for cards, sections, images entering.
- ease-in-out-quart (0.76 0 0.24 1) — symmetric, for things that leave and arrive (modals, sheets). Never ease-in an element that enters the viewport — eyes hate the slow start.
- back-out (0.34 1.56 0.64 1) — a single overshoot. One item to spotlight, not a grid of items.
- ease-out-elastic — celebration only. A handful of elements on the whole page, ever.
- custom snap — use a spring lab or overshoot preset when a UI element 'lands' (drag release, toggle thumb).
/* the 'expensive' entrance: fast to 80%, then glide */
.card-enter {
animation: rise .7s cubic-bezier(.16, 1, .3, 1) both;
}
@keyframes rise {
from { opacity: 0; transform: translateY(16px); }
to { opacity: 1; transform: none; }
}
/* overshoot for ONE hero element */
.spotlight {
transition: transform .5s cubic-bezier(.34, 1.56, .64, 1);
}Make timing a token
The reason motion feels inconsistent across pages is that durations and curves live in ten different files. At Motif every asset exposes duration/easing props so a whole page can be re-tuned from one place — do the same in your project with CSS variables.
:root {
--ease-enter: cubic-bezier(.16, 1, .3, 1);
--ease-snap: cubic-bezier(.34, 1.56, .64, 1);
--dur-fast: 140ms; /* hovers, focus */
--dur-base: 300ms; /* colour, opacity */
--dur-enter: 700ms; /* entrances */
}Respect the motion budget
prefers-reduced-motion doesn't mean 'no animation' — it means 'no vestibular-triggering animation'. Fades and 2px translate are fine; scale-bursts and parallax are not. Our reduced-motion fallbacks keep opacity/translate only.