CSS transition-behavior: Animate the Display Property
For years, animating elements entering or leaving the Document Object
Model (DOM) required cumbersome JavaScript timeouts or non-semantic CSS
visibility workarounds because the display property could
not be transitioned. The introduction of the CSS
transition-behavior property solves this issue by enabling
transitions on discrete properties like display and
content-visibility. This article explains how
transition-behavior works, why it is essential for modern
web animation, and how to implement it alongside
@starting-style to create seamless entry and exit
transitions using pure CSS.
The Problem with Discrete Properties
In CSS, properties traditionally fall into two categories:
interpolable and discrete. Interpolable properties, such as
opacity or transform, possess continuous
intermediate values that browsers can smoothly blend over time. In
contrast, discrete properties—such as display: none versus
display: block—switch abruptly with no mathematical
midpoint.
Historically, applying a transition to an element fading out to
display: none caused the element to disappear immediately,
cutting off any simultaneous opacity or transform transitions.
How
transition-behavior Works
The transition-behavior property, part of the CSS
Transitions Level 2 specification, dictates whether transitions should
apply to discrete properties. By default, its value is
normal, which excludes discrete properties from transition
effects.
When set to allow-discrete, the browser alters the
timing of state changes:
- During Exit Animations: The discrete property
change is deferred. If an element transitions from
display: blocktodisplay: nonealongside anopacityfade, the browser keepsdisplay: blockactive until the duration of all running transitions has elapsed, switching todisplay: noneonly at the very end. - During Entry Animations: The browser applies the visible display state immediately at the start of the transition, allowing the visual properties to transition smoothly into place.
You can set this property individually or via the
transition shorthand:
/* Individual property */
transition-behavior: allow-discrete;
/* Shorthand syntax */
transition: display 0.4s allow-discrete, opacity 0.4s;Implementing Entry and Exit Transitions
To transition an element entering the page from
display: none, the browser needs to know the styles the
element should transition from at the moment it becomes
visible. This is handled using the @starting-style
rule.
Here is a practical implementation of an element transitioning into and out of the view:
.card {
display: block;
opacity: 1;
transform: translateY(0);
transition:
opacity 0.3s ease,
transform 0.3s ease,
display 0.3s allow-discrete;
}
/* Hidden state */
.card.is-hidden {
display: none;
opacity: 0;
transform: translateY(20px);
}
/* Entry starting styles */
@starting-style {
.card {
opacity: 0;
transform: translateY(20px);
}
}In this setup:
- When
.is-hiddenis removed, the element switches todisplay: blockimmediately, starts with the values defined in@starting-style(opacity: 0), and transitions to fully visible. - When
.is-hiddenis added,opacityandtransformbegin animating immediately, while the change todisplay: noneis held until the 0.3-second transition finishes.
Benefits in Modern Development
The transition-behavior: allow-discrete property
simplifies component architecture. It reduces reliance on JavaScript
animation libraries and transitionend event listeners for
routine UI components like modals, accordions, tooltips, and dialogs.
Coupled with native elements like <dialog> and the
Popover API, it provides a performant, accessible, and native mechanism
for managing animated UI states directly in CSS.