How to Create Asymmetric Enter/Exit Timing in UI Animations

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, 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, the "Asymmetric timing" section explicitly mandates that deliberate actions animate slower while system responses snap immediately. The 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 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 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:

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. Use a trigger-anchored transform-origin to ensure the motion originates from the UI element that initiated the interaction, grounding the asymmetric timing spatially:

<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, 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:

/* 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:

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
  • Always pair with origin-aware transforms using var(--transform-origin) to anchor motion to the trigger element
  • Reference skills/review-animations/STANDARDS.md and 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, 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, 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.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →