Home / Snippets / Animation /

transition-behavior: allow-discrete

Animate display and visibility — properties that were never transitionable before. Toggle the checkboxes below.

display
visibility
New feature
animationno-js

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.