wandres.dev
ANIMAR LA ENTRADA · @starting-style y transition-behavior

transition-behavior: allow-discrete y la propiedad overlay

Cómo se habilita la transición de propiedades discretas, qué desbloquea exactamente, y el estado real de soporte de overlay en 2026.

⏱ 18 min

transition-behavior es la quinta propiedad de las transiciones, añadida por el nivel 2 de la especificación con un único valor útil: allow-discrete. Su trabajo es permitir que las propiedades discretas participen en una transición en lugar de cambiar de golpe, y con ella se desbloquean tres propiedades que resuelven el problema de la aparición: display, content-visibility y overlay. La tercera tiene un estado de soporte que hay que conocer antes de construir nada encima.

🎯 Al terminar esta lección sabrás
  • Escribir allow-discrete en las dos formas y saber cuál es preferible.
  • Enumerar las tres propiedades que desbloquea y qué resuelve cada una.
  • Describir qué hace overlay y qué ocurre en los motores que no la implementan.
  • Diagnosticar el error de orden que anula transition-behavior.

Las dos formas de escribirlo

Como palabra clave dentro de una entrada de la abreviatura, que es la forma recomendada:

.panel {
  transition:
    opacity 240ms ease-out,
    display 240ms allow-discrete;
}

Como propiedad independiente, que aplica a todas las entradas de la lista:

.panel {
  transition: opacity 240ms ease-out, display 240ms;
  transition-behavior: allow-discrete;
}

La primera es preferible por dos razones. La primera es que casi nunca quieres allow-discrete para todas las propiedades: lo quieres para la propiedad discreta concreta. La segunda es que la propiedad independiente tiene una trampa de orden que la abreviatura no tiene.

La trampa de orden

transition-behavior forma parte de la abreviatura transition. Como toda abreviatura de CSS, reinicia a su valor inicial las propiedades que la componen y que no menciones. Es decir, escribir transition: ... después de transition-behavior: allow-discrete borra el allow-discrete.

/* Roto: la abreviatura viene despues y reinicia el comportamiento. */
.mal {
  transition-behavior: allow-discrete;
  transition: opacity 240ms, display 240ms;
}

El síntoma es exactamente el mismo que si no hubieras escrito nada: el elemento desaparece de golpe. Y el CSS se lee correcto, porque las dos declaraciones están ahí. Es el tipo de error que sobrevive a varias revisiones de código.

La forma de comprobarlo sin dudar:

console.log(getComputedStyle(panel).transitionBehavior);
// "normal" si se ha perdido, "allow-discrete" si esta bien

Las tres propiedades que desbloquea

display. La más importante. Con allow-discrete, el motor aplica la regla especial que vimos en la lección anterior: el elemento se renderiza durante toda la transición, en las dos direcciones. Eso es lo que permite que un elemento salga del flujo por completo cuando está cerrado, sin perder la animación de salida.

content-visibility. Misma lógica, aplicada a la propiedad que permite al motor saltarse el renderizado de un subárbol. Sirve para listas y acordeones donde quieres el ahorro de content-visibility: hidden y a la vez una apertura animada.

.seccion-plegable {
  content-visibility: hidden;
  opacity: 0;
  transition:
    opacity 200ms ease-out,
    content-visibility 200ms allow-discrete;
}

.seccion-plegable[data-abierta] {
  content-visibility: visible;
  opacity: 1;
}

overlay. Es la propiedad más peculiar de las tres, porque no la puedes establecer tú: es una propiedad que solo controla el navegador, y lo único que puedes hacer con ella es listarla en transition-property para retrasar el momento en que su valor cambia.

Qué hace overlay exactamente

Cuando un elemento se muestra en la capa superior —un dialog abierto con showModal(), o un elemento con el atributo popover mostrado— el navegador lo saca de su posición normal en el orden de pintado y lo dibuja por encima de todo el documento, ignorando índices de apilamiento, recortes y desbordamientos.

Al cerrarlo, el navegador lo devuelve a su sitio inmediatamente. Y ahí está el problema: si el elemento tiene una transición de salida de 300 milisegundos, durante esos 300 milisegundos ya no está en la capa superior, así que puede quedar tapado por cualquier cosa que estuviera por encima de él en el documento, o recortado por un ancestro con overflow: hidden. El resultado típico es que la animación de salida no se ve, o se ve a medias.

overlay es la propiedad que representa “estoy en la capa superior”, y listarla en la transición con allow-discrete hace que su cambio se retrase hasta el final: el elemento se queda en la capa superior mientras dura la salida.

[popover] {
  opacity: 1;
  transition:
    opacity 240ms ease-out,
    display 240ms allow-discrete,
    overlay 240ms allow-discrete;

  @starting-style { opacity: 0; }
}

[popover]:not(:popover-open) {
  opacity: 0;
}
overlay es la única pieza del bloque que no es Baseline, y su ausencia falla de una forma que no notarás desarrollando

Aquí toca ser preciso con el soporte, porque las cuatro capacidades de este nivel se presentan siempre juntas y no están al mismo nivel. @starting-style y transition-behavior: allow-discrete alcanzaron Baseline en agosto de 2024 con Firefox 129, y están en los cuatro motores. overlay no: es una propiedad de disponibilidad limitada, implementada en Chromium y no en los otros motores. Escribirla no rompe nada —una declaración desconocida dentro de la lista de transition-property se ignora—, así que el código es seguro, pero el comportamiento difiere y hay que saber cómo. El detalle cruel es cuándo se manifiesta la diferencia: solo cuando el elemento de la capa superior está dentro de un contenedor que lo recortaría o lo taparía, es decir, en documentos con estructura real. En la página de pruebas donde estás desarrollando el diálogo, con el dialog colgando directamente del body y nada encima, la salida se ve perfecta en todos los motores y overlay parece innecesaria. Metes el mismo componente en una aplicación con una cabecera fija, un contenedor con overflow: hidden y un panel lateral con índice de apilamiento, y en Chromium sigue funcionando mientras que en los demás la animación de salida se corta a mitad o se ve por detrás de la cabecera. La defensa que funciona sin depender de overlay es de estructura, no de estilos: monta los elementos de capa superior lo más arriba posible del árbol y no los metas dentro de contenedores con recorte. Con esa disciplina, la ausencia de overlay deja de importar en la mayoría de los casos, y donde siga importando, la alternativa portable es acortar mucho la salida —por debajo de unos 150 milisegundos el corte apenas se percibe— o aceptar que en esos motores la salida sea instantánea, que es un fallo cosmético y no funcional.

El estado de soporte, en una tabla

Capacidad Estado en agosto de 2026
@starting-style Baseline desde agosto de 2024. Chrome y Edge 117, Safari 17.5, Firefox 129
transition-behavior: allow-discrete Baseline desde agosto de 2024. Chrome y Edge 117, Safari 17.4, Firefox 129
overlay Disponibilidad limitada. Solo Chromium
Atributo popover Baseline desde abril de 2024
Elemento dialog Baseline desde marzo de 2022

La estrategia que se deriva de la tabla es escribir el patrón completo, incluyendo overlay, y no depender de ella para que la interfaz funcione. Es una mejora, no un requisito.

Detectar el soporte cuando importe

Si tienes un caso donde la diferencia sí es funcional, la detección es directa:

/* Camino base: salida corta, se corta poco si no hay overlay. */
[popover] {
  transition:
    opacity 140ms linear,
    display 140ms allow-discrete;
}

/* Mejora: con overlay se puede permitir una salida mas larga. */
@supports (overlay: auto) {
  [popover] {
    transition:
      opacity 280ms ease-out,
      display 280ms allow-discrete,
      overlay 280ms allow-discrete;
  }
}

Y para comprobar desde JavaScript si el motor conoce la propiedad:

const tieneOverlay = CSS.supports('overlay', 'auto');

Fíjate en que @supports (overlay: auto) comprueba que el motor sabe analizar la propiedad, no que la implemente bien. Es la limitación habitual de la detección de características y en este caso es suficiente, porque no hay motores que la analicen sin implementarla.

⚔️ Comprueba el efecto de overlay
  1. Monta un popover con una transición de salida de 300 milisegundos, dentro de un contenedor con overflow: hidden y una cabecera con índice de apilamiento alto.
  2. Pruébalo con overlay en la lista de transición y sin ella, en Chromium. Anota la diferencia.
  3. Pruébalo en un motor que no la implemente y anota qué se ve.
  4. Mueve el elemento a un contenedor sin recorte y repite. Decide si en tu caso overlay es necesaria.