transition-behavior

Get affordable and hassle-free WordPress hosting plans with Cloudwaysstart 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:

Transitioning ::backdrops and overlays

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

  翻译: