Speed
Slow motion multiplier
1×
Reduced motion
Show

The exit gap

the brief overreaches here
Eleven components animate in and vanish out, and the brief calls fixing all eleven the highest-leverage change in the audit. Checked against the sources it cites, most of them are already right: an exit earns its place when it aids orientation, and a surface the user has already looked away from does not qualify. What survives is narrower and still worth doing — useLayer cannot express an exit at all, which blocks the four or five surfaces that genuinely want one.Decide here: Which surfaces earn an animated exit, and what enter and exit cost on the ones that do.
Not drift, and not a standoff — the sources settle this one
“Not everything needs an animated exit. Elements the user is moving away from, such as tooltips, hover cards, and dropdown menus, can disappear instantly. The user has already shifted their attention. Animate the exit only when it helps orient the user, like a panel closing or a dialog dismissing to reveal what’s underneath.”The eleven layer components are not drift. They are the published guidance, correctly followed — and it names tooltips, hover cards and dropdown menus specifically, which is most of the list. This is also the one conflict the sources settle: Emil’s guidance, which the brief cites as a reference, says high-frequency UI often should not animate its exit at all. Both sources agree with the page; neither supports the criterion.So this is not the usual which-document-wins problem. The brief’s own reference says high-frequency UI often should not animate its exit, and the published page says the same thing about the same surfaces. The rule the sources support is narrower:Animate an exit only when it aids orientation. When it is animated, its direction is spatially consistent with the entrance. Timing may be shorter when the dismissal is a system response, but is chosen by purpose and frequency rather than applied universally.Criterion 7 has been narrowed to match, which also resolves a contradiction inside the rubric: Tooltip is high-frequency enough that criterion 2 says give it no motion, while criterion 7 as written made an instant dismissal a blocker. Both could not hold.

Tune the presence budget

These two values drive every pane on this page, and every layer demo in the lab.
enter
230ms
exit
175ms
overlay
410ms
Exit ships shorter than enter, which is beUI’s rule — old content should leave faster than new arrives. Treat it as a default rather than a law. Emil scopes asymmetric timing to system responses versus deliberate actions, and a dialog the user chose to close and is watching may want the full enter duration. Drag them equal and judge it per surface rather than globally.

Which of the eleven actually earn an exit

Orientation is the test, not presence. Three of these are named in the published page as instant-dismissal surfaces — the rest is judgement, so argue with it.
earns one
Dialog, AlertDialog
Dismissal reveals the page underneath, and the user chose it deliberately. The published page names a dialog as its own example of an exit worth animating.
earns one
Lightbox
Full-screen. Without an exit the underlying page reappears with no explanation of where the image went.
earns one
BottomSheet, MobileNav
Spatially anchored to an edge, so the exit has an obvious direction and the entrance already established it.
earns one
CommandPalette
Modal and it covers content — but it is also a power-user surface hit many times a day, so this is the row most likely to flip on frequency grounds.
instant is fine
Tooltipnamed in the docs
The pointer has already left. Highest-frequency surface in the system, so criterion 2 argues against motion here even before orientation does.
instant is fine
HoverCardnamed in the docs
Same shape as Tooltip: dismissal is a side effect of the user moving somewhere else.
instant is fine
DropdownMenu, ContextMenunamed in the docs
The menu has done its job the moment a pick is made, and attention has moved to whatever the pick affected.
instant is fine
Popover
Not named on the page, but the same class. Worth a second look if a popover is ever used as a panel rather than a hint.
instant is fine
Selector, ComplexSelector, MultiSelector, Tokenizer
Dropdown-class. Attention is on the field and the value that just landed in it, not on the list closing.
instant is fine
Typeahead, PowerSearch, MoreMenu, Breadcrumbs overflow
Same. All of these are the user getting somewhere, and the surface closing is not the event.
Four surfaces earn an exit and seven do not, which inverts the brief’s framing: the fix is not eleven components short of a dismissal animation, it is a shared primitive that cannot express one for the four that want it. The work is the same size. The justification, and the rubric gate that follows from it, are not.

What an exit looks like on a surface that may not want one

All four demos below are surfaces the triage puts in the “instant is fine” column, and three are named in the published page. They are still worth looking at — you cannot judge whether a dismissal aids orientation without seeing the alternative — but read them as the question rather than the fix. The card that follows them, Dialog, is the clear case for.

Tooltip

useLayer · highest-frequency surface in the system
The published page names tooltips specifically as a surface that may vanish. Does the exit earn its 175ms here, on something seen hundreds of times a day?
Today — animates in, vanishes out
Tooltip
Proposed — the exit retraces the entry
Tooltip

HoverCard

useLayer · the most visible instance of the gap
A larger surface travelling the same distance. Judge the scale-from value here rather than on Tooltip — 0.96 reads differently at size.
Today — animates in, vanishes out
HoverCard
Proposed — the exit retraces the entry
HoverCard

Popover

useLayer
Click twice quickly. The exit has to retarget rather than restart, or rapid dismissal looks broken.
Today — animates in, vanishes out
Popover
Proposed — the exit retraces the entry
Popover

DropdownMenu

useLayer · submenus too
A menu snapping shut is the case people notice. Does the exit help, or does it just delay the next click?
Today — animates in, vanishes out
DropdownMenu
Proposed — the exit retraces the entry
DropdownMenu

Dialog, AlertDialog, CommandPalette

focus races the exit
Two separate bugs in one surface: the panel has no exit, and the backdrop has no transition at all. Watch the focus ring on the trigger.
Today — no exit, scrim hard-cuts, focus returns immediately
Delete workspace?This cannot be undone.
Proposed — panel and scrim both exit, focus waits
Delete workspace?This cannot be undone.
In the left pane the focus ring lights the instant the dialog closes, while the dialog is still on screen. With a real exit animation that means focus moves — and the page may scroll — mid-animation. The brief lists this as a risk; here it is a thing you can watch, and it has to be settled before the Dialog work starts rather than after.

The technique, running natively in this browser

Is the platform enough, or does the shared primitive need a JS fallback? This card answers it for real — nothing here is simulated.
A native popover transitioning display and overlay with allow-discrete, entering from @starting-style. This is the proposed implementation, not a mock-up of it.
Green across the row means the shared presence primitive can be built on the platform with no JS fallback, which is the question the spike exists to answer.

What one change buys

Every component below is a useLayer consumer, so all of them inherit a dismissal animation from a single definition. That is the leverage — and it is also the risk, because it is one change to the code path that owns dismissal.
TooltipHoverCardPopoverDropdownMenuContextMenuSelectorComplexSelectorPowerSearchTypeaheadMoreMenuBreadcrumbs overflow
layerAnimations — one definition, eleven consumers — css
.layer {
opacity: 0;
transform: scale(0.96) translateY(-4px);
transform-origin: var(--layer-origin);
transition:
opacity var(--duration-exit) var(--ease-exit),
transform var(--duration-exit) var(--ease-exit),
display var(--duration-exit) allow-discrete,
overlay var(--duration-exit) allow-discrete;
}
​
.layer:popover-open {
opacity: 1;
transform: scale(1) translateY(0);
transition:
opacity var(--duration-enter) var(--ease-entry),
transform var(--duration-enter) var(--ease-entry),
display var(--duration-enter) allow-discrete,
overlay var(--duration-enter) allow-discrete;
}
​
@starting-style {
.layer:popover-open { opacity: 0; transform: scale(0.96) translateY(-4px); }
}
useLayer — await the exit instead of hiding synchronously — tsx
- setOpen(false); // element gone this frame
+ setClosing(true); // stops accepting input immediately
+ await Promise.all(
+ el.getAnimations().map(a => a.finished),
+ );
+ setOpen(false); // focus return happens here

What this work has to be careful about

Dismissal is load-bearingLayer dismissal has its own invariants test for good reason. Keeping an element alive through its exit doubles the state matrix for focus return, outside-press and escape handling — and every one of those has to be tested against an element that is visible but already closing.
The published page already warns about this“Animations that block interaction are worse. If a user has to wait for a panel to finish sliding before they can click something inside it, the animation has become an obstacle.” The answer has to be that the surface stops accepting input the moment dismissal starts, not when the animation ends. Write that into the primitive’s contract rather than leaving it to each consumer.
Motion is already fighting the automated gatesTwo fixes landed this month to stop animation interfering with the accessibility audit and the visual-regression capture. More motion means more of this, and there is no documented policy for it. The lab’s reduced-motion switch doubles as the freeze mode a capture needs.