One component, one clock
Overlay and dialog share one duration and one curve.
Emil Kowalski@emilkowalskiMade a small dialog animation improvement at Linear: Matched the previously mismatched animation duration of overlay and the dialog + swapped spring animaton for an ease-out one so it feels less abrupt.
Durations are illustrative. Ranges and curve are from emil-design-eng.
Before and after
Open both, then close with Esc or the dim area.
Two clocksBad
Acme · Settings
Preferences
scrim 150ms ease · dialog spring(420, 20)Both on one clockGood
Acme · Settings
Preferences
scrim + dialog 200ms in · 150ms out.modal { --modal-duration: 200ms; --modal-ease: cubic-bezier(0.23, 1, 0.32, 1); }
.modal:not([data-open]) { --modal-duration: 150ms; }
.scrim {
opacity: 0;
transition: opacity var(--modal-duration) var(--modal-ease);
}
.dialog {
opacity: 0;
transform: translateY(8px) scale(0.95);
transition:
opacity var(--modal-duration) var(--modal-ease),
transform var(--modal-duration) var(--modal-ease);
}
.modal[data-open] .scrim { opacity: 1; }
.modal[data-open] .dialog { opacity: 1; transform: none; }
Set the two clocks
Pull the durations apart, press Play, then Match.
One dialog, two layersMismatched
Roadmap · Q4
Rename project
Δ 150msconst timing = { duration: 200, easing: "cubic-bezier(0.23, 1, 0.32, 1)", fill: "forwards" };
scrim.animate({ opacity: [0, 1] }, timing);
dialog.animate(
{ opacity: [0, 1], transform: ["translateY(8px) scale(0.95)", "none"] },
timing,
);
Frame by frame
Drag the scrubber, then switch to Close.
Two clocksBad
Acme · SettingsPreferences
Overlay0%
Dialog0%
Both on one clockGood
Acme · SettingsPreferences
Overlay0%
Dialog0%
// Before: two clocks
<Overlay transition={{ duration: 0.15 }} />
<Dialog transition={{ type: "spring", stiffness: 420, damping: 20 }} />
// After: one clock
const transition = { duration: 0.2, ease: [0.23, 1, 0.32, 1] };
<Overlay transition={transition} />
<Dialog transition={transition} />
Why it matters, pros and cons
Why it matters
Scrim and panel read as one object; if one ends first, the page looks broken.
Pros
- Reads as one object, not two.
- One token to tune, no drift.
- A known end for focus return and unmount.
Cons
- Springs have no fixed duration; use a duration-based spring.
- A 20-30ms stagger is fine if both end together.
- A dragged sheet's backdrop follows the finger, not a clock.