Motion

The scale that doesn't lie

5 min

The most common modal animation on the web conjures a dialog from a single pixel. That's not motion — that's teleportation.

scale(0) is everywhere. Open any component library, any design system tutorial, and the modal entrance is a clean expansion from zero. It looks intentional. It feels smooth in a Codepen. It's still wrong.

The conjuring problem

When you animate from scale(0), the element starts as a dimensionless point. For 300ms it grows from nothing — a singularity expanding into a dialog. Physically, nothing works like that.

Objects don't materialize. They arrive. They move from somewhere else, or they expand from a state you could already perceive. A drawer slides in. A tooltip fades up. A card lifts. The scale has to start somewhere visible.

scale(0) says: this thing didn't exist before I opened it. That's a lie about the UI's state. The modal was always there — it just wasn't shown.

What 3% gives you

scale(0.97) means the modal starts at 97% of its final size. Three percentage points. The difference in the demo below feels larger than the number suggests — because it changes the perceptual category.

At 0%, the element doesn't exist yet. At 97%, it already occupies space. The eye catches it before the animation finishes. What reads as appearing from nothing becomes arriving from nearby.

The perceptual target is: it was already here, now I'm seeing it clearly. That's the honest version of a modal entrance.

The 300ms mistake

Modals are interruptions. They stop what you were doing to ask for attention.

A 300ms entrance is friction added to a moment that already asks for patience. The user wants to read the modal — act on it, dismiss it, move on. They can't until the animation finishes. 300ms is not a long time in absolute terms. At the start of an interruption, it feels like exactly that.

180ms is the correct duration for modal entrances. Long enough to register as intentional motion. Short enough that it resolves before attention fully arrives.

The ease

The CSS default, ease, has a slow start and a slow end. For modals, the slow end compounds the 300ms problem — the dialog lingers in its near-final position for a perceptible moment before settling. It reads as hesitant.

ease-out starts fast and decelerates to a natural stop. The modal arrives with conviction and settles cleanly. This is how physical objects come to rest under deceleration, not oscillation.

.modal {
  transform-origin: center;
  animation: modalEnter 180ms ease-out both;
}

@keyframes modalEnter {
  from {
    transform: scale(0.97);
    opacity: 0;
  }
  to {
    transform: scale(1);
    opacity: 1;
  }
}
The correct modal entrance — 180ms, ease-out, scale(0.97), center origin.

Origin matters

Menus and dropdowns use transform-origin set to the trigger position. The animation expands from where the user clicked — that physical connection is part of the meaning.

Modals are different. They're not attached to any particular element. They emerge from the page itself, occupying the center of the viewport. Center origin is correct here — the modal expands symmetrically, lands exactly where it will sit, no visual jump.

This is why you shouldn't copy menu animation specs to modals. Same principle — high initial scale, ease-out — but different origin. The origin encodes the relationship between the component and its source.

Component Scale start Duration Ease Origin
Modal / dialog 0.95–0.97 180ms ease-out center
Dropdown menu 0.95 150ms ease-out trigger
Tooltip 0.95 120ms ease-out anchor
Scale entrances by component type — same principle, different parameters and origin.

The range

0.97 is not a magic number. It sits at the useful center of the valid range: 0.95–0.99.

Below 0.95, the motion begins to feel like growth from nothing — you've crossed back into conjuring territory. Above 0.99, you lose the perceptual snap: the user doesn't register that a transition happened. The element just appears, which is the same outcome as no animation at all.

0.97 is always visible, always snappy. The user's eye catches the modal before the animation ends. The motion reads as arrival, not appearance.

Pick a number in the range and be consistent. The exact value matters less than the principle: start at a scale the eye can already see.