dialog
Marca un contenedor como un diálogo modal o no modal. Utilice el elemento nativo <dialog> con showModal() — gestiona de forma automática el confinamiento del foco, la capa superior, el telón de fondo y el cierre con Escape. Recurra a role="dialog" solo cuando el elemento nativo sea imposible de usar.
Cuándo utilizarlo
Utilice <dialog> abierto mediante .showModal(). El elemento nativo es compatible con todos los navegadores actuales y gestiona automáticamente el fondo inactivo, el confinamiento del foco, el cierre con Escape, el gancho de estilos ::backdrop y la representación en la capa superior. Esto le ahorra alrededor de 200 líneas de JavaScript frágil.
Recurra a role="dialog" sobre un <div> únicamente cuando:
- Se admiten navegadores anteriores a Chrome 37 / Firefox 98 / Safari 15.4 (improbable en 2026).
- El diseño exige un diálogo que no pueda situarse en la capa superior (poco frecuente).
- Se está corrigiendo un modal personalizado inaccesible durante una refactorización.
Todo diálogo DEBE tener un nombre accesible (aria-labelledby apuntando a un encabezado visible es el patrón más sólido). Los diálogos modales también requieren aria-modal="true".
Contrato de teclado y foco
Según el patrón APG de diálogo modal:
- Al abrirse, el foco se desplaza al primer elemento enfocable dentro del diálogo (habitualmente el botón de cierre o el campo principal).
- Tab y Shift+Tab ciclan el foco DENTRO del diálogo — una «trampa de foco». Tab desde el último elemento regresa al primero; Shift+Tab desde el primero regresa al último.
- Escape cierra el diálogo.
- Al cerrarse, el foco regresa al elemento que abrió el diálogo. Guardar y restaurar el foco es imprescindible; los usuarios que pierden su posición se quedan sin referencia.
- El resto de la página permanece
inert(oaria-hidden="true"más no enfocable como alternativa) mientras el diálogo está abierto.
Fallos frecuentes
- Diálogo personalizado sin trampa de foco. Tab escapa del diálogo y va detrás de él; los usuarios pierden el contexto.
- Foco no restaurado al desencadenador al cerrar. El siguiente Tab aterriza en el cuerpo del documento.
- El contenido de fondo sigue siendo accesible mediante Tab aunque esté visualmente oculto detrás del diálogo.
- Sin nombre accesible. El diálogo se abre y el lector de pantalla anuncia únicamente «dialog».
- Diálogo personalizado sin
aria-modal="true"— la tecnología de asistencia lo trata como contenido en línea. - Escape cierra el diálogo, pero este se renderiza a través de un portal que pierde el foco de teclado durante la animación de cierre.
Ejemplo
<!-- Preferido -->
<dialog id="confirm">
<h2>¿Eliminar este borrador?</h2>
<p>Esta acción no se puede deshacer.</p>
<form method="dialog">
<button value="cancel">Cancelar</button>
<button value="delete" autofocus>Eliminar</button>
</form>
</dialog>
<script>
document.getElementById('confirm').showModal();
</script>
<!-- Cuando <dialog> es imposible -->
<div role="dialog" aria-modal="true" aria-labelledby="dlgTitle">
<h2 id="dlgTitle">Editar perfil</h2>
…
</div>