Entry animations become baseline web platform features

Two CSS features that enable entry animations for elements once hidden with display: none have reached the "Baseline Newly available" milestone. @starting-style and transition-behavior: allow-discrete were previously only supported in Chrome and Safari; with Firefox 129 now supporting both, the features can be used across all major browser engines. The capabilities were first highlighted in Chrome's announcement about four new CSS features for entry and exit effects.

Defining initial states with @starting-style

The @starting-style at-rule lets you specify the styles that apply to an element before it renders in the document. This is essential for elements that animate in from display: none: since the browser has no prior rendered state to work from, @starting-style provides the starting point that the transition can animate out of.

The rule works like other CSS at-rules—just nest your selectors inside it. For a <dialog> element, you would place dialog[open] within a @starting-style block to define the styles used when the dialog opens:

@starting-style {
  dialog[open] {
    /*   Styles before the dialog opens   */
  }
}

Toggling discrete properties mid-transition

Entry animations from display: none also require a new animation mode that supports discrete properties. Discrete properties cannot be interpolated smoothly—they behave like a switch, supporting only one value or the other. Examples include display, visibility, and mix-blend-mode.

With this new mode enabled, the browser swaps the property values at the 50% point of a transition rather than at the 0% point. You can enable it in one of two ways:

  • Using the standalone transition-behavior property with the value allow-discrete.
  • Including the allow-discrete keyword in the transition shorthand.

The shorthand approach is recommended. If you set transition-behavior: allow-discrete before a transition shorthand that declares the transition, timing, and easing functions, the browser will ignore the transition-behavior property entirely.

When working with the shorthand, you only need to add allow-discrete to the properties that require discrete animation. The following example transitions both translate and display, but applies allow-discrete only to display:

transition: translate 0.7s ease-out, display 0.7s ease-out allow-discrete;

Animating top-layer elements

These features are especially valuable for top-layer elements like <dialog> and components using the popover attribute. In the example below, a <dialog> starts off-screen with a 100vh vertical translation and moves to the viewport center once open, removing the translation in the process:

/* Before the dialog opens */
@starting-style {
  dialog[open] {
  translate: 0 100vh;
  }
}

/* Dialog is-open state */
dialog[open] {
  translate: 0 0;
  transition: translate 0.7s ease-out, display 0.7s ease-out allow-discrete;
}
The same logic can be written more concisely with CSS nesting, which also recently achieved Baseline status:

dialog[open] {
  translate: 0 0;
  transition: translate 0.7s ease-out, display 0.7s ease-out allow-discrete;

  @starting-style {
    translate: 0 100vh;
  }
}
Demo animating in a dialog element.

Progressive enhancement for existing elements

These new features are worth treating as progressive enhancements. Elements animating into the top layer, or from a display: none state, will simply appear without any transition in browsers that don't support the new capabilities—which is exactly how they behave today.

For guidance on exit effects with progressive enhancement, see the original article on four new CSS features for smooth entry and exit animations. Additional documentation is available for @starting-style on MDN and transition-behavior on MDN.