Que un popover entre deslizándose en vez de aparecer de golpe cuesta tres reglas de CSS y cero JavaScript, y eso funciona en Chrome, Firefox y Safari. Que vuelva a salir deslizándose es otra historia, y preparando esta nota descubrí que está más verde de lo que sugiere la etiqueta "Baseline". En la demo de arriba, abre el popover y el diálogo, añade y quita tarjetas, y desactiva @starting-style para ver la diferencia. Las etiquetas de arriba te dicen qué puede hacer tu navegador.
El problema que resuelve
MDN lo explica sin rodeos: las transiciones CSS no se disparan en la primera actualización de estilo de un elemento, ni cuando su display pasa de none a otro valor. Es a propósito, pero significa que un popover, un <dialog> o un nodo que acabas de añadir no tienen un valor "anterior" desde el que hacer la transición. Simplemente aparecen.
@starting-style se lo da. Define los valores desde los que hace la transición un elemento cuando recibe su primera actualización de estilo: al insertarse en el DOM o al pasar de display: none a visible. La salida es la otra mitad, y para eso hace falta transition-behavior: allow-discrete.
Cómo funciona
Este es el núcleo del popover de la demo:
/* 1. Dónde termina cuando está abierto */
.pop:popover-open { opacity: 1; transform: none; }
/* 2. Estado cerrado: hacia dónde va la salida */
.pop {
opacity: 0;
transform: translateY(28px) scale(.9);
transition:
opacity .5s, transform .5s,
overlay .5s allow-discrete,
display .5s allow-discrete;
}
/* 3. De dónde viene en el primer fotograma */
@starting-style {
.pop:popover-open { opacity: 0; transform: translateY(28px) scale(.9); }
}
Hay tres estados, y esa es la idea clave: cerrado, abierto y de partida. MDN muestra que no tienen por qué coincidir: el elemento entra desde los valores de @starting-style y sale hacia sus valores por defecto. En la demo coinciden, así que entra y sale por el mismo sitio.
Las piezas:
@starting-stylesolo se aplica en ese primer fotograma. Después se ignora, y por eso al cerrar no "vuelve" a él.- El orden importa. Según MDN, la regla
@starting-styley la regla original tienen la misma especificidad, así que pon@starting-styledespués o ganará la original. También puedes anidarla dentro de la regla, como hago con las tarjetas:.item { @starting-style { opacity: 0; } }. - Los pseudoelementos necesitan un bloque independiente. El anidamiento no funciona con ellos, así que el fondo del diálogo usa
@starting-style { dialog[open]::backdrop { ... } }por separado. display ... allow-discretees lo que hace visible la salida.displayes una propiedad discreta: por defecto no hace transición. Conallow-discrete, MDN explica que al pasar denoneablockel cambio ocurre al 0 % de la duración y al pasar anoneocurre al 100 %, así que el elemento sigue visible durante toda la animación. El valor inicial detransition-behavioresnormal.overlayes para elementos en el top layer (popovers abiertos, diálogos modales). No lo puedes fijar tú; lo fija el navegador. Añadirlo a la lista de transiciones retrasa la salida del top layer hasta que termina la animación.- Solo para transiciones. Si la entrada la haces con animaciones
@keyframes, no necesitas@starting-style.
El panel de tarjetas usa la misma regla para nodos creados con appendChild(). Para quitarlos, el patrón de siempre: añadir una clase que anima hacia el estado "de partida" y llamar a remove() cuando termina.
El panel de registro lista los eventos transitionrun que dispara el navegador. En Chromium, al cerrar el popover aparecen display y overlay en esa lista. En otros navegadores verás otra cosa.
Soporte y trampas
MDN marca @starting-style como Baseline 2024, disponible desde agosto de 2024. Sus datos de compatibilidad indican Chrome 117, Firefox 129 y Safari 17.5. La entrada es la parte segura.
La salida, no:
- Firefox todavía no hace transiciones de
display. Los datos de compatibilidad de MDN lo marcan como no soportado en Firefox y, por ese motivo, su soporte detransition-behaviorcomo parcial. El trabajo entró en el código de Mozilla este verano, pero detrás de una preferencia,layout.css.display-animations.enabled, que sigue afalsepor defecto en el código fuente. El 13 de octubre de 2026, un ingeniero de Mozilla avisó en el bug de seguimiento de que esto haría que la funcionalidadtransition-behaviorpasara de "Newly Available" a "Limited Availability". La página de MDN detransition-behaviorya muestra "Limited availability". - Safari hace transiciones de
display, pero no al salir del top layer. MDN indica Safari 18 para las transiciones dedisplay, yoverlaycomo exclusivo de Chromium (y experimental). En la versión de WebKit de Playwright con la que pruebo la demo, undivnormal se desvanece bien, mientras que el popover y el diálogo desaparecen en cuanto se cierran. El bug de WebKit para implementaroverlayy el nuevo algoritmo de salida del top layer sigue abierto. - No se puede detectar con
@supports.CSS.supports('transition-behavior', 'allow-discrete')devuelvetrueen los tres navegadores con los que he probado, incluso donde la salida no funciona. La demo hace una pequeña comprobación en tiempo de ejecución, adaptada de una que se publicó en el bug de Mozilla:
function supportsDisplayTransition() {
const div = document.createElement('div');
div.style.transition = 'display 1s allow-discrete';
document.body.append(div);
const cs = getComputedStyle(div);
cs.display; // fuerza el cálculo de estilos
div.style.display = 'none';
const ok = cs.display !== 'none'; // sigue visible = está en transición
div.remove();
return ok;
}
const nativeExit = supportsDisplayTransition() && CSS.supports('overlay', 'auto');
Si nativeExit es falso, los botones Cerrar de la demo, y Esc en el diálogo, animan el elemento con Element.animate() mientras sigue abierto y solo después llaman a hidePopover() o close(). Cerrar el popover haciendo clic fuera sigue siendo instantáneo en esos navegadores; no hay forma de retrasarlo. Mi primer plan B usaba una clase CSS en lugar de Element.animate(), y en WebKit la transición a veces no arrancaba cuando la disparaba Esc. La versión con Web Animations fue fiable en los tres.
- Movimiento. Con
prefers-reduced-motion, la demo mantiene los fundidos y quita las transformaciones y el desenfoque.
Qué vigilar
- Que Firefox active la preferencia. Cuando
layout.css.display-animations.enabledvenga activada por defecto, las salidas funcionarán allí con el mismo CSS. overlayen WebKit. Hasta entonces, Safari seguirá cortando en seco las salidas del top layer.- El estado Baseline de
transition-behavior. Si baja a "Limited", las herramientas que usan los datos de web-features empezarán a marcarlo. Las entradas con@starting-styleno se ven afectadas.