Guía educativa de HTML dialog
Pedir atención con un diálogo nativo
Una confirmación necesita una decisión; un inspector puede convivir con la página. Aprende a elegir entre modal y no modal, abrir y cerrar dialog, dirigir el foco y recuperar una respuesta sin construir tu propia capa de interacción.
Modelo mental
dialog aporta la semántica de una ventana de interacción. El método de apertura decide si bloquea el resto del documento.
showModal() sitúa el diálogo en la top layer y vuelve inerte el resto de su documento: no se puede pulsar ni enfocar el contenido exterior mientras siga abierto. El navegador gestiona esa modalidad; no hace falta recrearla con un z-index enorme ni un bucle de Tab.
show() abre una ventana no modal: recibe foco inicial, pero la página sigue disponible y Tab puede salir del diálogo. No se añade a la top layer ni genera el backdrop de un modal. Su posición se puede adaptar con CSS, como el panel en flujo de esta guía.
El atributo open indica que dialog está abierto, pero no dice que sea modal. Escribir <dialog open> lo muestra como no modal y no ejecuta los pasos de foco de show(). Para abrir usa la API o los comandos; para cerrar usa close() o un formulario de diálogo.
Abrir según la tarea
Una confirmación breve puede interrumpir el flujo. Un panel de consulta suele necesitar que el usuario continúe trabajando fuera.
Con command="show-modal" y commandfor, el botón apunta al id de dialog y solicita apertura modal sin un listener de apertura. La receta añade JavaScript solo para el fallback en navegadores sin comandos y para mostrar returnValue. No dispara showModal() una segunda vez cuando hay soporte nativo.
Confirmación modal con una respuesta
Última respuesta:
Panel de consulta sin bloquear la página
El HTML, CSS y JavaScript de cada receta forman un ejemplo completo. Los IDs son propios de la guía y no cambian el playground. Sin JS, la confirmación funciona si el navegador admite los comandos, aunque el resultado no se actualiza; la apertura no modal permanece deshabilitada.
No conviertas un diálogo ya abierto con show() en modal llamando a showModal(): puede lanzar InvalidStateError. Ciérralo primero y vuelve a abrirlo en el modo elegido. El guard dialog.open de las recetas evita repetir la apertura del mismo ejemplo.
Foco y elección del patrón
La modalidad define el recorrido, pero el contenido define dónde conviene empezar. Un nombre accesible y una salida visible siguen siendo necesarios.
aria-labelledby conecta el diálogo con su título visible. aria-describedby sirve aquí porque la explicación es breve; no lo apliques a todo un contenido largo con listas o tablas que necesita explorarse por estructura. dialog ya aporta su rol; showModal() aporta la modalidad accesible.
Usa autofocus en el control que tenga sentido al entrar. Esta confirmación empieza en Cancelar, la opción segura; un formulario puede empezar en su primer campo. Para texto largo, puede convenir un encabezado interior con tabindex="-1" como destino de foco. No añadas tabindex al elemento dialog ni fuerces el foco con cada render.
Al cerrar, el navegador puede devolver el foco al elemento que estaba enfocado antes de abrir. En el no modal no debe robarlo si el usuario ya se fue fuera. Comprueba el recorrido real; si el disparador desaparece tras la acción, dirige el foco a un destino lógico de la nueva pantalla.
dialog: tarea en una ventana
Elige modalidad para una decisión que necesita completarse o cancelarse antes de continuar. Usa show() para una ventana de interacción que puede convivir con la página. Reserva la interrupción para una necesidad concreta.
Popover: contenido complementario
Ayuda contextual, acciones o contenido temporal que no debe bloquear el documento. Popover ofrece top layer y modos de cierre, pero sigue siendo no modal: estar por encima no equivale a volver inerte lo demás.
details: contenido desplegable
Una explicación que se expande en el flujo mediante summary, sin una ventana de diálogo ni modalidad. Encaja cuando el contenido pertenece a esa sección y solo necesita mostrarse u ocultarse.
Cerrar y solicitar cierre
Ofrece siempre un control explícito. Escape, el toque exterior y una respuesta de formulario son caminos distintos, no una única señal de confirmación.
Por defecto, un modal acepta solicitudes de cierre como Escape y no se cierra al pulsar el exterior. El no modal no acepta Escape por defecto. No bases la salida únicamente en el teclado: conserva Cancelar o Cerrar para ratón, tacto y tecnologías de asistencia.
closedby="any"
Añade closedby="any" si quieres cierre exterior, solicitudes de la plataforma y mecanismos explícitos. Trátalo como mejora progresiva: sin soporte de closedby, el exterior no cerrará el modal, pero el botón y Escape siguen siendo salidas.
closedby="closerequest"
closedby="closerequest" acepta solicitudes de la plataforma y mecanismos explícitos, sin cierre exterior. Es el comportamiento modal por defecto cuando el atributo falta o no tiene un valor válido; no promete el mismo gesto en todas las plataformas.
closedby="none"
closedby="none" permite mecanismos explícitos y desactiva el cierre exterior y las solicitudes del usuario donde se admite. No lo uses para obligar a confirmar. Un navegador que ignore el atributo conserva su comportamiento por defecto.
cancel se dispara ante una solicitud de cierre, por ejemplo Escape. Se escucha directamente en dialog y puede impedirse con preventDefault() cuando sea necesario proteger trabajo pendiente; debe existir una salida clara. No se dispara simplemente por llamar a close() ni por enviar method="dialog".
close avisa de que el diálogo ya se ha cerrado; no es cancelable y no burbujea. Úsalo para recoger el resultado o limpiar recursos. close(valor) cierra directamente; requestClose(valor) solicita cierre y pasa por cancel, por lo que puede rechazarse. Detecta requestClose por separado antes de usarlo.
Formularios y resultado
Cerrar una ventana y guardar datos son operaciones distintas. La confirmación anterior solo comunica una elección local.
method="dialog" cierra el diálogo al enviar el formulario y pone el value del botón de envío en returnValue. No hace una petición de red ni borra los campos. formmethod="dialog" permite reservar este comportamiento para un botón de un formulario que use otro método.
La validación nativa sigue aplicándose al envío: un campo required vacío puede impedir el cierre. Por eso la acción Cancelar lleva formnovalidate; descartar no debe exigir completar un formulario. Un botón que solo abre o llama a close() debe tener type="button" para no enviar por accidente.
Lee returnValue en close, pero no asumas que cualquier cierre confirma. Escape o close() sin argumento pueden conservar un valor anterior. La receta lo reinicia antes de abrir. Para una acción real, procesa solo confirm como confirmación explícita; cancel y la cadena vacía son salidas sin confirmar.
Si necesitas guardar datos, envíalos y gestiona validación, espera y errores antes de cerrar. method="dialog" no sustituye a ese proceso. La guía no ejecuta una operación real: el resultado permite observar la diferencia entre elección y descarte.
Estilos sin perder comportamiento
El aspecto puede adaptarse al sitio sin reemplazar el funcionamiento nativo. Conserva espacio para texto, acciones y foco.
El navegador oculta dialog cuando no tiene open. Evita una regla incondicional dialog { display: grid; }, porque podría mostrarlo cerrado. Si necesitas cambiar su display, limita la regla a dialog[open]. El form interior de las recetas puede usar Grid sin tocar esa visibilidad.
dialog::backdrop pinta la capa situada detrás del modal en la top layer. No es un nodo del DOM, no añade cierre exterior y no aparece al abrir con show(). Cambiar su color solo ayuda a reconocer el contexto bloqueado.
Limita el ancho al viewport y permite scroll interior para ventanas bajas y texto ampliado. El panel y los botones deben envolver y conservar el foco visible. La receta usa propiedades lógicas y un límite de altura; no añade animaciones, de modo que la interacción también funciona con movimiento reducido.
Errores frecuentes
Reconoce el síntoma y corrige la causa antes de añadir otro overlay.
Se ve, pero la página sigue operable
- Lo que esperabas
- Mostrar un modal que bloquee el documento.
- Por qué ocurre
- open o show() abren un diálogo no modal; z-index y aria-modal no cambian eso.
- Cómo corregirlo
- Abre con showModal() o command="show-modal" y comprueba foco y Tab.
El botón declarativo no abre
- Lo que esperabas
- Abrir el diálogo desde HTML en cualquier navegador.
- Por qué ocurre
- Falta soporte de comandos o commandfor no coincide con un id único.
- Cómo corregirlo
- Revisa la asociación y detecta commandForElement. Usa showModal() como fallback local, sin ejecutar ambos caminos a la vez.
Cancelar exige completar campos
- Lo que esperabas
- Descartar una edición aunque todavía haya campos inválidos.
- Por qué ocurre
- Un botón submit dentro de method="dialog" también activa validación.
- Cómo corregirlo
- Añade formnovalidate a Cancelar o usa un botón type="button" que llame a close().
Escape parece repetir una confirmación
- Lo que esperabas
- Distinguir una confirmación nueva de un cierre sin elección.
- Por qué ocurre
- returnValue puede conservar el valor de la apertura anterior.
- Cómo corregirlo
- Reinícialo antes de abrir y procesa solo el valor explícito de la acción de confirmación.
El diálogo aparece incluso cerrado
- Lo que esperabas
- Ocultarlo al finalizar la tarea.
- Por qué ocurre
- Una regla display en dialog sobrescribe el estilo de ocultación del navegador.
- Cómo corregirlo
- Aplica display solo con [open] y cierra mediante la API, no quitando open a mano.
Soporte y mejora progresiva
El elemento, los comandos y las opciones de cierre tienen estados diferentes. Que el modal funcione no verifica todas sus extensiones.
dialog, show(), showModal() y method="dialog" forman la base Widely Available. Esta base permite una ventana nativa sin recrear modalidad o semántica. El lab separa HTML declarativo de JavaScript para explorar ambos caminos.
command y commandfor son Newly Available, Baseline desde el 12 de diciembre de 2025. La receta detecta commandForElement en HTMLButtonElement.prototype y usa showModal() cuando falta: el cierre por formulario sigue siendo nativo.
closedby tiene disponibilidad limitada: las fuentes consultadas registran soporte en Chromium y Firefox, pero no en Safari. La guía explica sus valores como mejora opcional y conserva el cierre explícito, sin prometer light dismiss donde no existe.
Comprueba typeof dialog.showModal === "function" para la apertura modal y "closedBy" in HTMLDialogElement.prototype para esa extensión. La presencia de un atributo en el HTML no demuestra que funcione; tampoco la tarjeta Baseline certifica este navegador ni una prueba de accesibilidad.
Para una tarea esencial sin soporte de dialog, ofrece el formulario o la confirmación en una página o sección convencional. Mostrar un bloque con CSS no reproduce top layer, inercia o restauración de foco. El contenido editorial y los bloques de código de esta guía se sirven completos sin JavaScript.