Saltar al contenido

CSS, de a poco

Curso de CSS

Dialog y popover

Lección 30 de 37 · 4 min

HTML ya trae modales y popovers con el teclado resuelto. CSS se encarga de lo que faltaba: que entren y salgan animados.

Modales con dialog

El elemento <dialog> es Baseline ampliamente disponible. Se abre desde JavaScript, de dos formas: showModal() lo abre como modal y show() lo abre sin modal, sin fondo y con la página todavía usable. Con showModal() el navegador te da gratis:

  1. Capa superiortop layerEl modal queda encima de todo, sin pelear con z-index ni con el overflow de sus padres.
  2. Fondo inerteinertEl resto de la página no recibe clics ni foco, así que Tab recorre solo el modal.
  3. EscapeEscLo cierra, y el foco vuelve al elemento que lo abrió.
  4. Telón::backdropUn pseudo-elemento detrás del modal que podés oscurecer.

Un <form method="dialog"> adentro cierra el modal al enviarse, sin una línea de JavaScript. Abrí cada uno con su botón, cerralo con Escape y mirá a dónde vuelve el foco. Una transición toma la duración del estado al que va: por eso la regla [open] lleva la de apertura y la regla base la de cierre, un 60% más corta, porque cerrar no debería hacer esperar.

Ver el código de
comprobando @starting-style…

Lago Moraine

Banff · octubre

Un lago turquesa rodeado de montañas y pinos

4 fotos en el álbum

¿Eliminar esta foto?

Lago Moraine

Se borra del álbum y de todos tus dispositivos. No se puede deshacer desde acá.

Compartir “Lago Moraine”

dialogo.css
.dialogo {
opacity: 0;
translate: 0 8px;
scale: 0.96;
transition-property: opacity, translate, scale;
transition-duration: 150ms; /* al cerrar */
transition-timing-function: cubic-bezier(0.22, 1, 0.36, 1);
}
.dialogo[open] {
opacity: 1;
translate: 0 0;
scale: 1;
transition-duration: 250ms; /* al abrir */
}
.dialogo::backdrop {
background-color: rgb(0 0 0 / 0);
transition-property: background-color;
transition-duration: 150ms;
transition-timing-function: cubic-bezier(0.22, 1, 0.36, 1);
}
.dialogo[open]::backdrop {
background-color: rgb(0 0 0 / 0.4);
transition-duration: 250ms;
}
/* sin soporte: abre y cierra sin animación */
@supports (transition-behavior: allow-discrete) {
.dialogo {
transition-property: opacity, translate, scale, display, overlay;
transition-behavior: allow-discrete;
}
.dialogo::backdrop {
transition-property: background-color, display, overlay;
transition-behavior: allow-discrete;
}
/* después de [open]: mismo peso, gana el último */
@starting-style {
.dialogo[open] {
opacity: 0;
translate: 0 8px;
scale: 0.96;
}
.dialogo[open]::backdrop {
background-color: rgb(0 0 0 / 0);
}
}
}
/* movimiento reducido: queda el fundido, sin desplazamiento */
@media (prefers-reduced-motion: reduce) {
.dialogo,
.dialogo[open] {
translate: none;
scale: none;
}
}
dialogo.html
<!-- se abre con dialog.showModal() -->
<dialog
class="m-auto rounded-xl bg-white p-4 dark:bg-neutral-900
opacity-0 motion-safe:translate-y-2 motion-safe:scale-96
open:opacity-100 open:translate-y-0 open:scale-100
transition-[opacity,translate,scale,display,overlay] transition-discrete duration-150 open:duration-250 ease-[cubic-bezier(0.22,1,0.36,1)]
starting:open:opacity-0 starting:open:motion-safe:translate-y-2 starting:open:motion-safe:scale-96
backdrop:bg-black/0 backdrop:transition-[background-color,display,overlay] backdrop:transition-discrete backdrop:duration-150 open:backdrop:duration-250 backdrop:ease-[cubic-bezier(0.22,1,0.36,1)]
open:backdrop:bg-black/40 starting:open:backdrop:bg-black/0"
>
<p>Contenido</p>
<form method="dialog">
<button>Cerrar</button>
</form>
</dialog>

El atributo popover

El atributo popover convierte cualquier elemento en un popover, y un botón con popovertarget lo abre y lo cierra sin JavaScript. Es Baseline 2025: anda en los navegadores actuales, pero conviene probarlo en los que tenga tu público.

aviso.html
<button popovertarget="aviso">Abrir</button>
<div id="aviso" popover>
Contenido
</div>
  1. Cierre ligeropopover="auto"Se cierra con un clic afuera o con Escape, y abrir otro popover auto cierra el anterior. Es lo que hace popover a secas.
  2. Manualpopover="manual"Solo se cierra con su botón o desde JavaScript, y pueden convivir varios abiertos.
  3. Abierto:popover-openSelecciona el popover mientras está abierto, como [open] en el modal.

El popover también va a la capa superior, pero no es modal: la página sigue usable. Usá el modal cuando la persona tiene que responder antes de seguir (confirmar, completar un formulario) y el popover para menús, ayudas y avisos que se pueden ignorar.

La misma página de fotos dos veces. Con showModal() todo queda oscurecido detrás de la confirmación y la página espera; con popover el menú de compartir cuelga de su botón y la página sigue usable.

Animar la entrada y la salida

El problema clásico: un elemento cerrado tiene display: none, y display no se transicionaba. Dos piezas Baseline 2024 lo resuelven. @starting-style define el estilo desde el que arranca la entrada, y transition-behavior: allow-discrete deja que display espere a que termine la salida. Sumá overlay para que el elemento no salga de la capa superior antes de tiempo; tiene disponibilidad limitada, así que donde no existe la salida pierde parte del efecto.

Un modal en tres momentos. En @starting-style arranca invisible y 8 píxeles más abajo; con [open] sube y aparece en 250 milisegundos sobre el fondo oscurecido; al cerrar, allow-discrete hace que display: none espere los 150 milisegundos del fundido.
dialogo.css
.dialogo {
opacity: 0;
transition-property: opacity, display, overlay;
transition-duration: 200ms;
transition-behavior: allow-discrete;
}
.dialogo[open] {
opacity: 1;
}
@starting-style {
.dialogo[open] {
opacity: 0;
}
}
  1. El orden importa@starting-styleVa después de la regla [open], porque tienen el mismo peso y gana la última.
  2. Ojo con el atajotransitionDevuelve transition-behavior a normal: escribí allow-discrete después, o adentro del atajo.
  3. Mejora progresiva@supportsSin soporte, el modal abre y cierra sin animación. Por eso el CSS del demo agrupa lo nuevo en @supports.

Con prefers-reduced-motion: reduce, dejá el fundido y sacá el desplazamiento y la escala. Y si ves :open, es Baseline 2026: hace lo mismo que [open], que hoy anda en todos lados.

En Tailwind

Tailwind v4 tiene una variante para cada pieza. open: cubre [open], :popover-open y :open a la vez, así que las mismas clases sirven para el modal y para el popover.

CSSTailwind
[open] · :popover-openopen:
@starting-stylestarting:open:opacity-0
transition-behavior: allow-discretetransition-discrete
transition-property: opacity, display, overlaytransition-[opacity,display,overlay]
::backdropopen:backdrop:bg-black/40
prefers-reduced-motion: no-preferencemotion-safe:
aviso.html
<button popovertarget="aviso">Abrir</button>
<div id="aviso" popover
class="m-auto rounded-xl bg-white p-4
opacity-0 open:opacity-100 starting:open:opacity-0
transition-[opacity,display,overlay] transition-discrete duration-200"
>
Contenido
</div>