Get affordable and hassle-free WordPress hosting plans with Cloudways — start your free trial today.
The transition-behavior
property allows us to make transitions between discrete properties, such as display
or visibility
. And what we mean by “discrete” is that there’s no way to go from, say, a display
value of none
to a display
value of block
since those are not numeric values that we can interpolate between.
While the transition-behavior
property doesn’t make these properties interpolable, we can control when they change to better fine-tune the transition across all its duration.
dialog {
transition: opacity 500ms ease-out, display 500ms, overlay 500ms;
transition-behavior: allow-discrete;
}
For example, using transition-behavior
, we could create exit transitions for elements whose display
is set to none
, like in a dialog closing down:
Syntax
transition-behavior: <transition-behavior-value> = normal | allow-discrete
- Initial value:
normal
- Applies to: all elements
- Inherited: no
- Percentages: N/A
- Computed value: as specified
- Canonical order: per grammar
- Animation: not animatable
Values
/* Default value */
transition-behavior: normal;
/* Allow discrete */
transition-behavior: allow-discrete;
/* Global values */
transition-behavior: inherit;
transition-behavior: initial;
transition-behavior: revert;
transition-behavior: revert-layer;
transition-behavior: unset;
normal
: Transitions will not be started for discrete properties, only for interpolable properties.allow-discrete
: Transitions will be started for discrete properties as well as interpolable properties.
With this new property, the transition
shorthand changes, allowing a new value for transition-behavior
at the end:
transition: [transition-property] [transition-duration] [transition-timing-function] [transition-delay]
[transition-behavior];
Interpolable and discrete properties
Any time you read a technical explanation of a CSS property, you will often find a section for the property’s Animation behavior in its specification table. For properties like color
or opacity
, it is defined as by computed value type. For others like the font-family
and display
properties, there is a discrete animation type since their values are keywords rather than numbers. Finally, properties like animation
or transition
are not animatable at all. The primary difference between these three types of properties depends on whether CSS can interpolate between their values.
In CSS, interpolation is going from Value A to Value B while providing values in between. For example, the opacity
property can be interpolated because if we change its value (e.g. from 1 to 0) we know what values are included in that range. In the case of the color
property, we can transition or animate from one color to another using the values located in between the first color and the second color we’re transitioning to. Properties with an animation by computed value type can be interpolated, and thus are smoothly animatable.
Some properties take a list, like background-position
, but are still animatable. The animation will depend on the initial value and the final number of items in the list.
Properties with a discrete animation can’t be interpolated. For example, there are no in-between values for an element that goes from display: none
to display: block
, so the property abruptly switches. The moment the switch happens depends on the property: the display
and content-visibility
properties will switch at the beginning of the transition, while the rest will switch at the transition’s halfway (50%) mark. This is why we are unable to transition an element from display: block
to display: none
— the value would just switch immediately, hiding the rest of the transition since there’s nothing to interpolate.
The transition-behavior
property, introduced in the CSS Transitions Module Level 2 specification, allows us to transition discrete properties. While it doesn’t make them interpolable, we can control when they switch, so the transition is visible across its duration.
For the sake of completeness, there are also properties that are not animatable. Those are usually the animation
and transition
properties since they can’t be applied to themselves. If you want to know the animation type of all properties, you can check this full list by Vallek.
Basic usage
While transition-behavior
will be used mostly for exit transition involving display:none
(as we will see later), it’s important to note that it can be used to control the transitions for all discrete properties. Take for example the justify-content
property. Usually, it abruptly switches whenever its value changes, so it would be impossible to wait for a transition to happen before switching. Something like adding the property to the transition
list won’t make any difference.
transition: justify-content 2s;
As you can see in the following demo if you click the button to toggle between justify-content: space-between
and justify-content: center
, the property won’t wait for the blocks background-color
to transition.
For the transition to work such that the switch happens after two seconds, we have to set the transition-behavior
to its allow-discrete
value. Additionally, since the justify-content
property switches when the transition is 50% completed, we need to set its transition duration to 4s
so it switches at 2s
. Alternatively, we could also add a 2s
delay.
transition: justify-content 4s;
transition-behavior: allow-discrete;
/* or */
transition: justify-content 2s 2s;
transition-behavior: allow-discrete;
This way, first the block’s transition happens and immediately then, the justify-content
value switches:
Exit transitions
The transition-behavior
property’s main appeal is using for “exit” transitions, as that usally involves transitioning the display
property from something like a value of block
to none
. As we’ve learned, this is a discrete animation since there’s no way to interpolate between those two values right out of the box. Take for example the following dialog
element:
<dialog>
<p>This is dialog with (hopefully) transitions</p>
<form method="dialog">
<button>Close</button>
</form>
</dialog>
<button class="open">Open Dialog</button>
To show it, we will need a little of JavaScript to call its showModal()
method whenever the button is clicked:
const dialog = document.querySelector("dialog");
const openButton = document.querySelector(".open");
openButton.addEventListener("click", () => {
dialog.showModal();
});
As you can see, closing the dialog happens instantly, but let’s say we want it to fade out. To achieve that, we style the dialog when it’s open using the dialog[open]
selector and give it an open state (opacity: 1
), and in the common dialog
selector, we set its exit state (opacity: 0
).
Now, to transition between both states, we add opacity to the transition list, along with the display
property set with an allow-discrete
value. This makes display
switch values at the end of the transition.
dialog[open] {
opacity: 1;
}
dialog {
opacity: 0;
transition: opacity 500ms ease-out, display 500ms allow-discrete;
}
Remember that the display
and content-visibility
properties don’t switch at the 50% mark. In this case, they switch at the end of the transition, so we don’t have to give it a delay or double the transition duration.
We3 can do the same thing with an entry transition, just in reverse! For this, though, we also need to work with the @starting-style
at-rule because it allows us to apply a style at the start of an animation. We have to define the entry styles for the dialog
:
dialog[open] {
opacity: 1;
@starting-style {
/* Entry Styles */
opacity: 0;
}
}
This will give it an opacity: 0
when it initially renders:
::backdrop
s and overlay
s
Transitioning When an element is positioned on the top layer of a stacking context — like a dialog
element or an element using the Popover API — a ::backdrop
pseudo-element that covers the entire viewport is rendered behind, which can be used to darken the background while a dialog is open. Picking up from the last example, we can apply what we know about discrete properties to also transition the ::backdrop
.
dialog::backdrop {
background-color: #0000;
transition: background-color 500ms ease-out, display 500ms allow-discrete;
}
dialog[open]::backdrop {
background-color: #0008;
}
@starting-style {
dialog[open]::backdrop {
background-color: #0000;
}
}
However, you will notice that the ::backdrop
exit transition doesn’t work, and it immediately disappears:
This is because we aren’t transitioning the overlay
property, which is another discrete property that determines whether an element is rendered in the top layer. What makes it unusual is that it can only be set by the browser, like when opening or closing a dialog
. What we can do, however, is mingle with its transition, making sure the element won’t go out the top layer until the other transitions finish.
dialog {
opacity: 0;
transition: opacity 500ms ease-out, display 500ms allow-discrete, overlay 500ms allow-discrete;
}
And now, the ::backdrop
that usually disappears once the element isn’t anymore on the top layer, will wait for the transition to finish:
Demo
Specification
The transition-behavior
property is defined in the CSS Transitions Level 2 specification.
Browser support
More information
- Four new CSS features for smooth entry and exit animations (Una Kravets & Joey Arhar)