Choosing Easing for Enter and Exit Motion
Part of Timing Functions & Easing Curves in Core CSS Animation Fundamentals.
The problem
A design system uses one easing, ease-in-out, and one duration, 300ms, for everything. Menus open sluggishly because they start slowly. Dialogs close with a lingering tail, so the page behind feels blocked for longer than necessary. Toasts drift in and drift out with the same weight, which makes dismissing one feel like it did not register. None of the individual animations is wrong, but the interface feels slower than its durations suggest.
Perceived speed depends on where in the animation the motion happens, not only on its total length.
Root cause analysis: attention is at the start of an entrance and the end of an exit
Entrances should decelerate. When something appears, the user wants to see it and read it. An ease-out curve covers most of the distance in the first part of the duration and slows into the final position, so content is nearly in place almost immediately and the settling motion is what remains. It reads as responsive. An ease-in entrance does the opposite — nothing seems to happen, then the element rushes in at the end — which reads as lag.
Exits should accelerate. When something leaves, the user has finished with it and their attention has moved on. An ease-in curve starts gently and speeds away, so the element seems to get out of the way decisively. An ease-out exit leaves the element crawling slowly off screen at the end, occupying attention it no longer deserves.
Moves within the view use both. An element that starts and ends visible — a card reordering, a panel resizing, a shared element morphing — is watched throughout, so it should accelerate out of its start and decelerate into its end: ease-in-out.
Exits should be shorter. Because nobody watches an exit closely, it can be quicker than the entrance. A common ratio is about two-thirds: a 240ms entrance with a 160ms exit. The asymmetry makes the interface feel faster without making entrances abrupt.
Step-by-step resolution
Production code pattern
:root {
--ease-enter: cubic-bezier(0.2, 0.8, 0.2, 1); /* decelerate */
--ease-exit: cubic-bezier(0.4, 0, 1, 1); /* accelerate */
--ease-move: cubic-bezier(0.4, 0, 0.2, 1); /* both */
--dur-enter-md: 240ms;
--dur-exit-md: 160ms; /* ~2/3 of enter */
--dur-move-md: 280ms;
}
.popover {
/* Closed state: this transition runs when CLOSING, so it uses the exit curve. */
opacity: 0;
translate: 0 6px;
transition:
opacity var(--dur-exit-md) var(--ease-exit),
translate var(--dur-exit-md) var(--ease-exit),
visibility var(--dur-exit-md);
visibility: hidden;
}
.popover[data-open] {
/* Open state: this transition runs when OPENING, so it uses the enter curve. */
opacity: 1;
translate: 0 0;
visibility: visible;
transition:
opacity var(--dur-enter-md) var(--ease-enter),
translate var(--dur-enter-md) var(--ease-enter),
visibility var(--dur-enter-md);
}
/* A card that moves within the list uses the move curve both ways. */
.card { transition: translate var(--dur-move-md) var(--ease-move); }
@media (prefers-reduced-motion: reduce) {
.popover { translate: none; transition: opacity 90ms var(--ease-exit), visibility 90ms; }
.popover[data-open] { transition: opacity 140ms var(--ease-enter), visibility 140ms; }
.card { transition: none; }
}
Rendering Impact: composite. Easing and duration change when intermediate values occur, not what is animated; the popover and card animate
opacityandtranslateonly.
The comments on the popover rules are there because the direction is counter-intuitive: the transition declared on the closed state is the one that runs when the popover closes, since the state being entered owns the timing. Putting the exit curve on the open state is the most common mistake when applying this pattern.
Springs, overshoot and emphasis
Deceleration curves can overshoot for playful entrances — a control point above 1 makes the element pass its final position and settle back, like cubic-bezier(0.34, 1.4, 0.64, 1). Use overshoot sparingly: for small elements that benefit from feeling physical, such as badges, toggles and floating buttons, and never for large surfaces, where the bounce moves a big area of the screen. Exits should not overshoot at all; bouncing away from the viewer reads as indecision. The physics behind spring-like curves is covered in approximating spring physics with CSS easing.
Verification checklist
Constraints and trade-offs
- A single shared curve is simpler to maintain but makes the interface feel slower.
- Very short exits can look like disappearance rather than motion; keep at least around 100ms.
- Overshooting entrances can clip inside containers with
overflow: clip. - Interrupting an entrance with an exit uses the exit curve from the current position, which can feel abrupt.
- Brand guidelines may define signature curves; map them onto enter, exit and move roles rather than using one everywhere.
Frequently asked questions
Should entrances use ease-in or ease-out?
Ease-out. It moves the element most of the way quickly and settles gently, so content is visible almost immediately.
Why should exits be faster than entrances?
Users are not watching an element leave as closely as one arriving. A shorter exit gets it out of the way and makes the interface feel quicker.
What easing should an element moving on screen use?
Ease-in-out, because it is visible at both ends: it should accelerate from rest and decelerate into its new position.
Where do I put the exit easing in CSS?
On the state being entered when closing, which is usually the closed or default rule. The transition values of the new state control the transition.
Related
- Timing Functions & Easing Curves — the parent topic
- How to Calculate Cubic-Bezier for Natural Motion — building custom curves
- Building a Duration and Easing Token Scale — turning these curves into tokens