Easing curves for UI animation: why the default feels generic, and what to use instead
Easing decides how a movement speeds up and slows down. The browser's default, ease, is a compromise that suits nothing in particular. Most interfaces need just three curves: a fast-starting ease-out for feedback and things arriving, an ease-in for things leaving, and an ease-in-out for things moving from one place to another.
Why easing matters
Nothing in the physical world starts or stops instantly. A drawer accelerates as you pull it and slows as it reaches the end of its runners. Animators have long called this slow in and slow out, one of the classic principles of character animation, and it's why linear motion on screen looks mechanical. Easing curves for UI animation are how CSS describes that acceleration: a mapping from time elapsed to progress made.
The curve changes how fast an interface feels more than the duration does. Two transitions of 250ms can feel sluggish or snappy depending on whether the movement happens mostly at the start or mostly at the end.
The built-in keywords
| Keyword | Curve | How it feels |
|---|---|---|
| linear | No easing | Mechanical. Right for spinners and progress bars, wrong for almost anything else. |
| ease (the default) | cubic-bezier(0.25, 0.1, 0.25, 1) | A quick start and long, soft finish. Pleasant, vague, recognisable. |
| ease-in | cubic-bezier(0.42, 0, 1, 1) | Starts slow, ends fast. Feels laggy for anything appearing. |
| ease-out | cubic-bezier(0, 0, 0.58, 1) | Starts fast, ends slow. Responsive, but the built-in version is mild. |
| ease-in-out | cubic-bezier(0.42, 0, 0.58, 1) | Symmetrical. Gentle, slightly sleepy at short durations. |
The trouble with ease isn't that it's ugly. It's that it's what you get when nobody chose, so a site that uses it for every hover, menu and reveal has transitions that all feel the same and none that feel designed. The built-in curves are also fairly flat: the real character comes from steeper custom curves.
Three curves for most interfaces
- Feedback and arrivals: a strong ease-out
- Something like
cubic-bezier(0.2, 0, 0, 1). Most of the change happens in the first few frames, so a button press or a menu opening responds immediately, then settles. Use it for hovers, presses, dropdowns, toasts and anything entering the screen. - Departures: an ease-in
- Something like
cubic-bezier(0.4, 0, 1, 1), used briefly. Things leaving can accelerate away; the visitor has already moved on, so a slow start doesn't cost anything. Keep it shorter than the matching entrance. - Moves: an ease-in-out
- Something like
cubic-bezier(0.65, 0, 0.35, 1). For an element travelling from one position to another on screen, such as a sliding panel or a reordered card, both ends should soften.
Large reveals, such as a section rising into view, often want a longer, more pronounced ease-out, for example cubic-bezier(0.16, 1, 0.3, 1), which covers most of the distance quickly and then glides the last few pixels. Curves whose second control point goes above 1, such as cubic-bezier(0.34, 1.56, 0.64, 1), overshoot and spring back. They suit playful moments and small elements; on large panels they feel unstable.
Durations to go with them
- 100 to 200ms for direct feedback: hover, press, toggle, focus.
- 200 to 300ms for small elements appearing: menus, tooltips, dropdowns.
- 300 to 500ms for larger panels and page-level changes.
- 500 to 800ms only for one-off reveals that nobody waits on, such as a hero image settling on first load.
Anything a person triggers repeatedly should be quick, because they'll see it dozens of times. Longer durations belong to motion that happens once. On phones, slightly shorter durations often feel better, since the distances travelled are smaller.
Writing them once
Name the curves as custom properties, such as --ease-out, --ease-in and --ease-move, alongside two or three durations, and use only those. A consistent set of curves is a large part of what makes motion feel like it belongs to one product. For physics-like springs that bezier curves can't express, the newer linear() easing function accepts a list of points and is supported in current major browsers; generate the points with a tool rather than by hand.
Whatever you choose, wrap movement in a check for reduced motion, so visitors who've asked for less movement don't get it.
How Anticode handles easing
The Critic flags default ease timing curves on every build, one of its nineteen checks. Pages from the design engine use their own curves instead: a fast ease-out, cubic-bezier(0.2, 0, 0, 1) over 150ms, for interface feedback, and a long glide, cubic-bezier(0.16, 1, 0.3, 1) over roughly 0.7 to 1.1 seconds, for the hero's entrance and images settling. The Playful system adds an overshooting curve so its sunflower shapes land with a small spring, which is where that kind of curve belongs.
The check catches the keyword, not the judgement: a custom curve that's too slow for a hover still passes. The code editor doesn't keep CSS changes beyond the colour tokens, so hand-added motion belongs in an app build or an exported copy; there, reuse the existing curves rather than introducing new ones.
Questions people ask
Should I use ease-out or ease-in-out?
Ease-out for things appearing or responding to a press; ease-in-out for things moving from one place to another on screen.
What is the default easing in CSS?
ease, equivalent to cubic-bezier(0.25, 0.1, 0.25, 1). It applies whenever a transition or animation doesn't name a timing function.
Why do my animations feel slow even at 300ms?
Usually the curve, not the duration. An ease-in or ease-in-out starts slowly, so the first visible movement comes late. Switch feedback to a strong ease-out.
Can CSS do spring animations?
Bezier curves can overshoot once. For a true spring with several bounces, use the linear() easing function with generated points.