@starting-style / transition-behavior: allow-discrete
The modern CSS pair that lets dialogs, popovers, and display-none elements animate both as they appear and as they disappear.
See it
What it is
'@starting-style' supplies the before-change style an element needs when it first appears or returns from 'display: none'. Without it, a new dialog or popover has no earlier rendered state for a CSS transition to start from. 'transition-behavior: allow-discrete' is the companion that lets discrete properties such as 'display' participate in the transition.
Together they make native entry and exit motion practical for dialogs, popovers, and other top-layer UI. Chrome shipped both in version 117 in 2023, which is why this pattern replaced years of JavaScript timeout-then-unmount workarounds only recently. On entry, 'display' flips to the visible value at the start; on exit, it flips to 'none' at the end, so the opacity or transform transition remains visible. Transitioning 'overlay' as a discrete property can likewise keep top-layer content in place until its exit finishes.
Gotcha: a standalone '@starting-style' rule has normal cascade rules. Put it after the matching open-state rule when the selectors have equal specificity, or the open state can override it before the transition begins. These features create the CSS lifecycle, but focus management and dialog semantics still belong to the native dialog or popover API.
Ask AI for it
Animate this native dialog with CSS only. Give 'dialog[open]' opacity 1 and transform scale(1), then add a later '@starting-style' block where 'dialog[open]' starts at opacity 0 and scale(0.96). Set the closed styles to opacity 0 and scale(0.98), transition opacity and transform for 180ms, include display and overlay in the transition list, and set 'transition-behavior: allow-discrete' so both discrete properties wait through the exit. Keep the native showModal(), close(), focus trap, and Escape behavior, and remove the motion under 'prefers-reduced-motion: reduce'.