· 9 min
El poder de <dialog>: modales nativos sin complicaciones
- HTML
- A11y
- Patrones UI
- dialog
El elemento nativo <dialog> se ha convertido en un pequeño gran aliado para los desarrolladores front-end.
Es nativo, estable y resuelve muchos problemas comunes: gestiona el foco, proporciona su propio fondo y ofrece métodos tan simples como show(), showModal() y close().
Menos código, menos dependencias y una experiencia de usuario más fluida.
Desde el punto de vista de la accesibilidad, <dialog> hace gran parte del trabajo por ti, pero los detalles siguen siendo importantes:
necesitas un título claro (visible o con aria-label), un orden de tabulación lógico, devolver el foco al disparador y bloquear la interacción con el fondo.
Los overlays personalizados suelen fallar en alguno de estos puntos.
Modal vs. No modal
- Modal accesible →
showModal()bloquea el fondo; enfoca el primer control útil o usaautofocus. - No modal →
show()deja el fondo accesible, ideal para banners o notificaciones flotantes. - Confirmaciones sin complicarte → con
method="dialog", los botones cierran el modal y establecen unreturnValueautomáticamente.
Consejo rápido: los formularios con
method="dialog"son perfectos para confirmaciones breves.
Cierran el modal automáticamente y devuelven el valor del botón pulsado.
<dialog id="confirm">
<form method="dialog" class="stack">
<h1 class="title">Confirmar borrado</h1>
<p>¿Seguro que quieres borrar este elemento?</p>
<menu class="row">
<button value="cancel">Cancelar</button>
<button value="confirm" autofocus>Aceptar</button>
</menu>
</form>
</dialog>
<button type="button" id="open">Abrir</button>
<script>
const dlg = document.getElementById('confirm');
open.addEventListener('click', () => dlg.showModal());
dlg.addEventListener('close', () => {
if (dlg.returnValue === 'confirm') {
// Ejecutar acción
}
open.focus(); // Devuelve el foco al disparador
});
</script>
Estilos útiles:
dialog:modalse aplica solo cuando el diálogo es modal;
dialog::backdropte proporciona un fondo oscurecido sin necesidad de overlays adicionales.
dialog:modal {
border: 0;
border-radius: 12px;
max-width: min(640px, 90vw);
padding: 1.25rem;
box-shadow: 0 10px 30px rgb(0 0 0 / 0.25);
}
dialog::backdrop {
background: rgb(0 0 0 / 0.55);
backdrop-filter: blur(2px);
}
Animaciones suaves sin JS
¿Quieres transiciones suaves sin JavaScript?
Usa el atributo open y deja que CSS se encargue de todo.
dialog {
opacity: 0;
transform: translateY(8px) scale(.98);
transition: opacity .16s ease, transform .16s ease;
}
dialog[open] {
opacity: 1;
transform: translateY(0) scale(1);
}
Si el fondo se mueve al abrir un modal, bloquea el scroll del body con :has():
body:has(dialog[open]) {
overflow: hidden;
}
Evitar cierres accidentales
Para evitar cierres accidentales, cancela el evento cancel (Esc) solo si tienes un botón visible para cerrar.
Y si no quieres que un clic en el fondo cierre el diálogo, comprueba si el evento apunta al propio <dialog> antes de cerrarlo.
<dialog id="edit">
<!-- contenido -->
</dialog>
<script>
const edit = document.getElementById('edit');
edit.addEventListener('cancel', (e) => {
e.preventDefault(); // Evita el cierre con ESC si no hay botón visible
});
edit.addEventListener('pointerdown', (e) => {
if (e.target === edit) edit.close('backdrop');
});
</script>
Checklist rápido
- Top layer → los modales abiertos con
showModal()viven en la capa superior del navegador: elz-indexno tiene efecto aquí. - Foco inicial → usa
autofocussi el primer control es evidente; de lo contrario, gestiona el foco manualmente al abrir y cerrar. - Título accesible → usa
aria-labelledbypara asociar el título visible oaria-labelsi no hay uno claro. - Nada de anidar → no puedes abrir un diálogo dentro de otro; ciérralo o reutilízalo antes de abrir uno nuevo.
Antes de publicar, verifica:
- Se abre y cierra correctamente con el teclado.
- El lector de pantalla anuncia el título.
- El foco vuelve al elemento disparador.
- Si se puede hacer clic en el fondo, hay otra forma de cerrar.
- El scroll del body se bloquea si el diseño lo requiere.
Conclusión
Si cuidas foco, cierre visible y título accesible, <dialog> te da modales nativos sólidos sin dependencias extra. Añade un buen retorno de foco, evita bloqueos del teclado y acompaña con estilos de fondo/animación para que la experiencia sea clara y fluida. Con eso, puedes reemplazar muchas librerías de modales y mantener el control en tu propio código.
Más detalles y ejemplos oficiales: MDN Web Docs - HTMLDialogElement
