How to animate opening a dialog or popover
Short answer
Describe the open state, the starting state with @starting-style, and allow display to animate:
<style>
dialog { opacity: 0; translate: 0 20px;
transition: opacity 0.3s, translate 0.3s, display 0.3s allow-discrete, overlay 0.3s allow-discrete; }
dialog[open] { opacity: 1; translate: 0 0; }
@starting-style { dialog[open] { opacity: 0; translate: 0 20px; } }
</style>
<button onclick="hello.showModal()">Open</button>
<dialog id="hello">
<p>Hello! This window fades in.</p>
<form method="dialog"><button>Close</button></form>
</dialog>How it works:
@starting-stylesets the style the element starts its transition from when it appears. Without it the browser has nothing to animate from: the element wasdisplay: none.allow-discreteondisplaydelays hiding until the transition ends, so closing is smooth too.overlaykeeps the dialog in the top layer while the closing animation runs.
A popover
For popover everything is the same, only the open state is the :popover-open pseudo-class:
<style>
[popover] { opacity: 0; scale: 0.9;
transition: opacity 0.25s, scale 0.25s, display 0.25s allow-discrete, overlay 0.25s allow-discrete; }
[popover]:popover-open { opacity: 1; scale: 1; }
@starting-style { [popover]:popover-open { opacity: 0; scale: 0.9; } }
</style>
<button popovertarget="menu">Menu</button>
<div id="menu" popover>Settings · Profile · Log out</div>The backdrop
::backdrop is animated the same way: give it a transparent background by default, a semi-transparent one for dialog[open]::backdrop, the same transition and a starting state in @starting-style.
Common mistake
Putting @starting-style before the open-state rule with the same specificity. The @starting-style block does not raise specificity, and the rule below it wins — the entry animation disappears. Write @starting-style after the main styles, as in the examples. In browsers without support the window simply opens without animation.
Functions used in this recipe
- @starting-style — Sets the styles an element starts from when it first appears, so a `transition` can animate its entrance.
- transition-behavior — Allows transitions of discrete properties such as `display` and `overlay`, so an element can fade out before it disappears.
- <dialog> — A dialog box; a modal one is opened with `showModal()` and blocks the rest of the page.
- popover — Turns an element into a layer that pops up over the page — without JavaScript.
- transition — Animates the change of a property from the old value to the new one.