# How to Implement Asymmetric Enter/Exit Timing in UI Animations

> Learn to implement asymmetric enter/exit timing for UI animations. Create intuitive interactions with slower user-driven animations and faster system responses.

- Repository: [Emil Kowalski/skills](https://github.com/emilkowalski/skills)
- Tags: tutorial
- Published: 2026-08-04

---

**Asymmetric enter/exit timing uses slower animations (200–400 ms) for deliberate user actions and faster animations (80–150 ms) for system responses to create natural, intuitive interactions.**

This technique is a core standard in the `emilkowalski/skills` repository, which defines animation best practices for modern React applications. By distinguishing between the **deliberate phase** (when a user presses, holds, or confirms) and the **system response** (the immediate feedback that follows), you can craft interfaces that feel both responsive and polished.

## Core Principles of Asymmetric Timing

The repository’s guidelines in [`skills/review-animations/STANDARDS.md`](https://github.com/emilkowalski/skills/blob/main/skills/review-animations/STANDARDS.md) establish that symmetric timing on press-and-release interactions feels mechanical. Instead, animations should follow the psychological weight of the action.

### Deliberate Actions vs. System Responses

According to [`skills/emil-design-eng/SKILL.md`](https://github.com/emilkowalski/skills/blob/main/skills/emil-design-eng/SKILL.md), you must first identify the interaction type:

- **Deliberate actions** include opening a modal, expanding a menu, or confirming a destructive operation. These require **slower, richer** animations to acknowledge the user’s intent.
- **System responses** include toast dismissals, quick feedback states, or closing actions. These should **snap quickly** to maintain interface responsiveness.

### Duration and Easing Guidelines

The [`skills/review-animations/SKILL.md`](https://github.com/emilkowalski/skills/blob/main/skills/review-animations/SKILL.md) file specifies concrete timing values:

- **Enter (deliberate)**: Use durations between **200–400 ms** with gentle easing such as `ease-out` or a spring with low stiffness.
- **Exit (response)**: Use short durations between **80–150 ms** with sharp easing like `ease-in` or a high-stiffness spring.

As noted in [`skills/review-animations/STANDARDS.md`](https://github.com/emilkowalski/skills/blob/main/skills/review-animations/STANDARDS.md), no hard-coded formula exists for every component. The exact values require iterative tweaking until the "deliberate-slow / snap-fast" rhythm feels natural.

## Implementing Asymmetric Timing in Framer Motion

The [`skills/pick-ui-library/SKILL.md`](https://github.com/emilkowalski/skills/blob/main/skills/pick-ui-library/SKILL.md) identifies **Framer Motion** (`motion` package) as the preferred library for UI-level animations. Define separate motion variants for `enter` and `exit` states:

```tsx
import { motion } from "motion/react";

const variants = {
  // Deliberate entry (slow, easing-out)
  enter: {
    opacity: 1,
    scale: 1,
    transition: {
      duration: 0.35,        // ≈ 350 ms
      ease: "easeOut",
    },
  },

  // System response exit (fast, easing-in)
  exit: {
    opacity: 0,
    scale: 0.95,
    transition: {
      duration: 0.12,        // ≈ 120 ms
      ease: "easeIn",
    },
  },
};

export const Popover = ({ isOpen, onClose }) => (
  <motion.div
    className="popover"
    initial={{ opacity: 0, scale: 0.9 }}
    animate={isOpen ? "enter" : "exit"}
    variants={variants}
    style={{ transformOrigin: "var(--transform-origin)" }}
  >
    {/* content */}
    <button onClick={onClose}>Close</button>
  </motion.div>
);

```

The `enter` variant uses a longer duration with `easeOut` to create a sense of weight, while the `exit` variant snaps quickly with `easeIn` to clear the interface efficiently.

## CSS-Only Implementation Strategy

If you cannot use Framer Motion, implement asymmetric timing using CSS classes and transitions. The repository standards support this fallback approach:

```css
/* Deliberate entry - slow and gentle */
.popover.entering {
  opacity: 1;
  transform: scale(1);
  transition: opacity 350ms ease-out, transform 350ms ease-out;
}

/* System response exit - fast and sharp */
.popover.exiting {
  opacity: 0;
  transform: scale(0.95);
  transition: opacity 120ms ease-in, transform 120ms ease-in;
}

/* Anchor origin to the trigger element */
.popover {
  transform-origin: var(--transform-origin);
}

```

Set the `--transform-origin` custom property via JavaScript based on the trigger element’s position to maintain spatial consistency.

## Advanced Patterns: Staggered Lists with Asymmetric Timing

For list components where multiple items enter and exit, apply asymmetric timing at the individual item level while staggering the deliberate entrance:

```tsx
const listVariants = {
  hidden: {},
  visible: {
    transition: {
      staggerChildren: 0.05,
    },
  },
};

const itemVariants = {
  hidden: { opacity: 0, y: 10 },
  visible: {
    opacity: 1,
    y: 0,
    transition: {
      duration: 0.3,
      ease: "easeOut",
    },
  },
  exit: {
    opacity: 0,
    y: -10,
    transition: {
      duration: 0.12,
      ease: "easeIn",
    },
  },
};

<motion.ul variants={listVariants} initial="hidden" animate="visible">
  {items.map(item => (
    <motion.li key={item.id} variants={itemVariants}>
      {item.content}
    </motion.li>
  ))}
</motion.ul>

```

Each list item inherits the asymmetric pattern: deliberate entry at 300 ms with `easeOut` and rapid exit at 120 ms with `easeIn`.

## Spatial Consistency with Origin-Aware Animation

Asymmetric timing must pair with **origin-aware animation** to feel grounded. According to [`skills/animation-vocabulary/SKILL.md`](https://github.com/emilkowalski/skills/blob/main/skills/animation-vocabulary/SKILL.md) and [`skills/review-animations/SKILL.md`](https://github.com/emilkowalski/skills/blob/main/skills/review-animations/SKILL.md), use a trigger-anchored `transform-origin` so animations emanate from the UI element that initiated the motion.

Set `transform-origin: var(--transform-origin)` in your styles and calculate the origin value based on the trigger element’s coordinates. This spatial consistency prevents the asymmetric timing from feeling disconnected or floating.

## Summary

- **Asymmetric enter/exit timing** distinguishes deliberate user actions (slow, 200–400 ms) from system responses (fast, 80–150 ms).
- The `emilkowalski/skills` repository defines these standards in [`skills/review-animations/STANDARDS.md`](https://github.com/emilkowalski/skills/blob/main/skills/review-animations/STANDARDS.md) and [`skills/emil-design-eng/SKILL.md`](https://github.com/emilkowalski/skills/blob/main/skills/emil-design-eng/SKILL.md).
- Use **Framer Motion** variants with separate `enter` and `exit` transition configurations as the primary implementation method.
- Maintain **spatial consistency** by anchoring `transform-origin` to the trigger element.
- Iterate on timing values through trial-and-error rather than relying on rigid formulas.

## Frequently Asked Questions

### What is the recommended duration ratio between enter and exit animations?

The repository suggests roughly a **3:1 to 4:1 ratio**, with enter animations typically lasting 200–400 ms and exit animations lasting 80–150 ms. However, [`skills/review-animations/STANDARDS.md`](https://github.com/emilkowalski/skills/blob/main/skills/review-animations/STANDARDS.md) emphasizes that these are starting points; the exact values require testing the component in isolation until the rhythm feels natural.

### Why should deliberate actions animate slower than system responses?

Slower enter animations acknowledge the **psychological weight** of a deliberate choice, giving users time to perceive that their intent has been registered. Fast exit animations respect the user’s time by clearing the interface immediately after the system responds, preventing the interface from feeling sluggish or unresponsive.

### Can I use CSS transitions instead of Framer Motion for asymmetric timing?

Yes. While [`skills/pick-ui-library/SKILL.md`](https://github.com/emilkowalski/skills/blob/main/skills/pick-ui-library/SKILL.md) recommends Framer Motion for complex UI animations, the asymmetric timing principle works with any animation system. Use CSS classes (like `.entering` and `.exiting`) with different `transition` properties to achieve the same 200–400 ms deliberate entry and 80–150 ms system exit timing.

### How do I determine the exact timing values for my specific component?

The repository guidelines explicitly state that no universal formula exists. You should **run the component in isolation** and adjust durations iteratively. Test with real users or observe the interaction yourself—the goal is a "deliberate-slow / snap-fast" rhythm that feels physically natural for your specific interaction pattern.