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

> Learn to create asymmetric enter/exit timing in UI animations. Craft intentional and responsive interactions with custom animation durations for user actions and system responses.

- Repository: [Emil Kowalski/skills](https://github.com/emilkowalski/skills)
- Tags: how-to-guide
- Published: 2026-07-31

---

**Asymmetric enter/exit timing assigns slower, richer animations (200–400 ms) to deliberate user actions and fast, snappy transitions (80–150 ms) to system responses, creating interactions that feel both intentional and responsive.**

The `emilkowalski/skills` repository defines asymmetric enter/exit timing as a fundamental principle for distinguishing between *deliberate* user input and *system* feedback. According to [`review-animations/STANDARDS.md`](https://github.com/emilkowalski/skills/blob/main/review-animations/STANDARDS.md), this approach requires separate motion profiles: enter animations should emphasize the user's conscious choice, while exit animations should acknowledge the action instantly without delay.

## Core Principles from the Repository

The asymmetric timing philosophy is documented across several skill files in the repository. In [`skills/review-animations/STANDARDS.md`](https://github.com/emilkowalski/skills/blob/main/skills/review-animations/STANDARDS.md), the "Asymmetric timing" section explicitly mandates that deliberate actions animate slower while system responses snap immediately. The [`skills/review-animations/SKILL.md`](https://github.com/emilkowalski/skills/blob/main/skills/review-animations/SKILL.md) file lists "Asymmetric enter/exit" as a core review criterion, flagging symmetric timing on press-and-release interactions as a finding that requires correction.

For detailed implementation guidance, [`skills/emil-design-eng/SKILL.md`](https://github.com/emilkowalski/skills/blob/main/skills/emil-design-eng/SKILL.md) contains a dedicated "### Asymmetric enter/exit timing" section that expands on the psychological rationale—slower entrances signal intentionality, while rapid exits confirm system receipt of the command.

## Implementation Steps

### 1. Categorize the Interaction Type

First, identify whether the animation represents a **deliberate user action** or a **system response**:

- **Deliberate**: Button presses that open modals, confirmation dialogs, menu expansions triggered by user intent
- **System response**: Toast dismissals, quick feedback notifications, automatic UI updates following an action

### 2. Define Separate Duration and Easing Values

Configure distinct motion parameters for each phase:

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

### 3. Apply Using Framer Motion

The [`skills/pick-ui-library/SKILL.md`](https://github.com/emilkowalski/skills/blob/main/skills/pick-ui-library/SKILL.md) specifies **Framer Motion** (imported as `motion` from `motion/react`) as the preferred library for UI-level animations. Define separate 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}
  >
    {/* content */}
    <button onClick={onClose}>Close</button>
  </motion.div>
);

```

### 4. Maintain Origin-Aware Transforms

Pair asymmetric timing with **origin-aware animation** as specified in [`skills/animation-vocabulary/SKILL.md`](https://github.com/emilkowalski/skills/blob/main/skills/animation-vocabulary/SKILL.md). Use a trigger-anchored `transform-origin` to ensure the motion originates from the UI element that initiated the interaction, grounding the asymmetric timing spatially:

```tsx
<motion.div
  variants={variants}
  style={{ transformOrigin: "var(--transform-origin)" }} // trigger-anchored
>

```

### 5. Iterate and Test

Adjust durations through trial-and-error until the rhythm feels natural. As noted in [`skills/review-animations/STANDARDS.md`](https://github.com/emilkowalski/skills/blob/main/skills/review-animations/STANDARDS.md), no universal formula exists—test components in isolation to calibrate the "deliberate-slow / snap-fast" relationship for your specific interface.

## Alternative Implementations

### CSS-Only Fallback

When JavaScript animation libraries are unavailable, implement asymmetric timing using CSS classes:

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

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

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

```

### Staggered List Entrances

For lists where each item enters deliberately but exits quickly as a group:

```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>

```

## Summary

- **Asymmetric enter/exit timing** creates natural interactions by distinguishing deliberate user actions from system responses
- **Enter animations** should use 200–400 ms durations with `ease-out` easing to emphasize intentionality
- **Exit animations** should use 80–150 ms durations with `ease-in` easing for immediate feedback
- **Framer Motion** (`motion/react`) is the recommended implementation library according to [`skills/pick-ui-library/SKILL.md`](https://github.com/emilkowalski/skills/blob/main/skills/pick-ui-library/SKILL.md)
- Always pair with **origin-aware transforms** using `var(--transform-origin)` to anchor motion to the trigger element
- Reference [`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) for review criteria and detailed specifications

## Frequently Asked Questions

### What is the difference between asymmetric and symmetric animation timing?

**Symmetric timing** uses identical durations and easing for both entering and exiting states, which can feel mechanical or slow to respond. **Asymmetric timing** deliberately varies these values—typically slower entrances (200–400 ms) for deliberate actions and faster exits (80–150 ms) for system responses—creating a rhythm that matches user expectations for intent versus feedback.

### Why does the repository recommend Framer Motion for asymmetric timing?

According to [`skills/pick-ui-library/SKILL.md`](https://github.com/emilkowalski/skills/blob/main/skills/pick-ui-library/SKILL.md), Framer Motion (referenced as the `motion` package) provides declarative variant APIs that cleanly separate `enter` and `exit` states with distinct transition configurations. This architectural separation makes implementing asymmetric durations and easing values more maintainable than imperative animation approaches.

### How do you handle transform-origin with asymmetric animations?

As documented in [`skills/animation-vocabulary/SKILL.md`](https://github.com/emilkowalski/skills/blob/main/skills/animation-vocabulary/SKILL.md), you should implement **origin-aware animation** by setting `transform-origin: var(--transform-origin)` in your CSS or inline styles. This CSS variable should be calculated based on the trigger element's position, ensuring the asymmetric motion appears to emanate from the user's point of interaction rather than the element's geometric center.

### Can you use CSS-only implementations for asymmetric enter/exit timing?

Yes, while Framer Motion is preferred, you can implement asymmetric timing using CSS classes with different `transition` properties. Define separate `.entering` and `.exiting` classes with distinct duration values (e.g., 350 ms for entry and 120 ms for exit) and apply them conditionally based on the component's state.