transition-behavior: allow-discrete
Animate display and visibility — properties that were never transitionable before. Toggle the checkboxes below.
Quick implementation
/* Fade out and remove from layout */
.element {
opacity: 1;
transition: opacity 0.4s ease, display 0.4s ease;
transition-behavior: allow-discrete;
}
.element.hidden {
opacity: 0;
display: none;
}
/* Entry animation with @starting-style */
@starting-style {
.element {
opacity: 0;
}
}
/* Visibility variant (stays in layout) */
.element-vis {
visibility: visible;
opacity: 1;
transition: opacity 0.4s ease, visibility 0.4s ease;
transition-behavior: allow-discrete;
}
.element-vis.hidden {
visibility: hidden;
opacity: 0;
}
@media (prefers-reduced-motion: reduce) {
.element,
.element-vis {
transition-duration: 0.01s;
}
}
Prompt this to your LLM
Includes role, constraints, two framework variants, and edge cases to handle.
You are a senior frontend engineer building show/hide UI patterns.
Goal: Animate an element's exit to display: none (and its entrance from display: none) using only CSS — no JavaScript animation libraries.
Technical constraints:
- Use transition-behavior: allow-discrete to opt display into the transition.
- Pair with opacity for a fade effect — opacity transitions continuously while display flips at the end.
- Use @starting-style to define the entry state so the element fades in when first rendered.
- Use oklch() for all color values — no hex or rgba().
- Transition duration 0.3–0.5s with ease timing.
Framework variant (pick one):
A) Vanilla HTML + CSS — toggle a .hidden class with a checkbox hack or minimal JS.
B) React component — accept isVisible prop, conditionally apply .hidden class, let CSS handle the animation.
Edge cases to handle:
- Respect prefers-reduced-motion: reduce duration to near-instant.
- Browsers without transition-behavior support will snap between states — ensure no broken layout.
- Use the visibility variant instead of display if the element must stay in layout (e.g. for screen readers).
Return HTML + CSS.
Why this matters in 2026
For decades, animating display: none was impossible — the property was discrete, so it snapped instantly. Developers resorted to JavaScript, visibility hacks, or max-height workarounds. transition-behavior: allow-discrete finally lets the browser hold a discrete property at its old value until continuous properties finish transitioning, then flip it. Combined with @starting-style, you get full entry and exit animations in pure CSS.
The logic
When you add transition-behavior: allow-discrete, the browser treats display (or visibility) as part of the transition timeline. On exit, opacity fades to 0 first, then display: none kicks in at the end of the duration. On entry, @starting-style defines the initial state (e.g. opacity: 0), so the element starts invisible and transitions to its final values. The display property flips to its visible value at the start of entry, giving opacity something to transition from.
Accessibility & performance
Elements with display: none are removed from the accessibility tree, which is correct for truly hidden content. If you need the element to remain screen-reader-accessible while visually hidden, use the visibility variant instead. opacity transitions are compositor-friendly and cheap. Gate all animations behind prefers-reduced-motion for users who prefer minimal motion.