CSS transition-behavior: allow-discrete for Dialogs

The CSS transition-behavior: allow-discrete property enables animations and transitions on discrete CSS properties—such as display and overlay—that traditionally could not be transitioned. In HTML <dialog> animations, this property ensures that a dialog remains visible in the DOM during its closing animation, allowing smooth exit transitions before switching to display: none, without needing complex JavaScript timeouts or event listeners.

The Problem with Dialog Exit Animations

By default, the native HTML <dialog> element toggles between display: block when open and display: none when closed. Properties with discrete values do not have intermediate states for CSS to interpolate.

When a user closes a dialog, the browser immediately applies display: none. Even if you define an exit transition on properties like opacity or transform, the abrupt switch to display: none hides the element instantly, cutting off any exit animations before they can play.

How allow-discrete Solves the Issue

The transition-behavior property accepts two values: normal (the default) and allow-discrete.

When set to allow-discrete, the browser alters how it handles the transition of discrete properties:

  • On entry: The property (such as display) switches immediately to its visible state at 0% of the transition duration so the element is rendered and can animate into view.
  • On exit: The property delays switching to its hidden state (display: none) until the entire transition duration has finished.

This allows accompanying transition effects, such as fading out or scaling down, to play to completion while the dialog remains rendered.

Key Companion Features

To make full use of transition-behavior: allow-discrete for <dialog> animations, it is typically paired with two other modern CSS capabilities:

  1. @starting-style: Defines the initial visual state of the dialog before it opens, enabling smooth entry transitions from display: none.
  2. The overlay property: Manages the top layer transition, ensuring the dialog does not prematurely drop out of the browser's top layer before the exit animation finishes.

Implementation Example

You can apply transition-behavior: allow-discrete individually to specific properties using the longhand syntax or inline within the transition shorthand.

dialog {
  opacity: 0;
  transform: scale(0.9);
  
  /* Shorthand declaration: transition property, duration, timing, behavior */
  transition: 
    opacity 0.3s ease-out,
    transform 0.3s ease-out,
    overlay 0.3s ease-out allow-discrete,
    display 0.3s ease-out allow-discrete;
}

/* Open state */
dialog[open] {
  opacity: 1;
  transform: scale(1);
}

/* Entry starting style */
@starting-style {
  dialog[open] {
    opacity: 0;
    transform: scale(0.9);
  }
}

In this setup, when dialog.close() is called or the open attribute is removed, display and overlay wait the specified 0.3s before changing, giving the opacity and transform properties the time required to complete their exit transitions.