Core · Avisos y estado
Diálogo modal
Pide tomar una decisión antes de continuar, mediante un elemento dialog nativo.
Ejemplo
Un botón que abre un diálogo para preguntar si se debe eliminar un borrador.
A native <dialog>.
Escape closes it and focus goes back to the button that opened it. Nothing is deleted here.
Problemas conocidos
Algunos navegadores compatibles aún no admiten los atributos command y commandfor. En esos navegadores, si JavaScript está desactivado, el botón de apertura no hace nada.
Ofrece a quien lee otra forma de realizar la misma acción.
Código
Copiar el marcado
Un dialog sin clase no necesita ninguna: tiene una superficie y está centrado.
<button type="button" commandfor="rename" command="show-modal">Rename project</button>
<dialog id="rename" aria-labelledby="rename-title">
<h2 id="rename-title">Rename project</h2>
<form method="dialog">
<label for="rename-name">New name</label>
<input id="rename-name" type="text" autofocus>
<button type="button" commandfor="rename" command="close">Cancel</button>
<button value="rename">Rename project</button>
</form>
</dialog>
.luz-modal añade la cabecera con su botón de cerrar, el cuerpo y el pie, la disposición en pantallas pequeñas y el desvanecimiento. Con la clase:
<button class="luz-btn luz-btn--primary" type="button" commandfor="rename" command="show-modal">Rename project</button>
<dialog class="luz-modal" id="rename" aria-labelledby="rename-title">
<div class="luz-modal__header">
<button class="luz-modal__close" type="button" commandfor="rename" command="close" aria-label="Close dialog">×</button>
<h2 class="luz-modal__title" id="rename-title">Rename project</h2>
</div>
<form method="dialog">
<div class="luz-modal__body">
<div class="luz-field">
<label class="luz-field__label" for="rename-name">New name</label>
<input class="luz-input" id="rename-name" type="text" autofocus>
</div>
</div>
<div class="luz-modal__footer">
<button class="luz-btn luz-btn--ghost" type="button" commandfor="rename" command="close">Cancel</button>
<button class="luz-btn luz-btn--primary" value="rename">Rename project</button>
</div>
</form>
</dialog>
import { initModals } from "@luz-ds/core/modal";
initModals();
Variantes y opciones
Un tamaño y ningún modificador: hasta 600 px de ancho y el 90 % de la altura de la ventana gráfica, con desplazamiento interno si el contenido es más largo. Hasta 640 px de ancho, o con menos de 30 rem de alto, ocupa toda la pantalla.
| Clase o atributo | Qué hace |
|---|---|
.luz-modal | La superficie del diálogo, con un fondo atenuado y ligeramente difuminado |
.luz-modal, __title | La fila del título, con una línea debajo |
.luz-modal | El botón ×, al final de la fila del título, de al menos 28 px (44 px cuando el diálogo ocupa toda la pantalla) |
.luz-modal | El contenido |
.luz-modal | Los botones, al final de la fila, con una línea por encima |
command="show-modal", command="close" | Abre y cierra el diálogo indicado en commandfor |
Cómo funciona
Un elemento nativo <dialog> con estilos de components/modal.css, que se abre como diálogo modal.
- El navegador se encarga de lo difícil: la página del fondo queda inerte, el foco permanece dentro, Escape lo cierra y el foco vuelve al botón que lo abrió.
- Los botones lo abren y lo cierran con
commandycommandfor, sin scripts. initModals()demodal.jsgestiona los mismos dos comandos en los navegadores que aún no los admiten, y no hace nada en los demás.luz.jslo ejecuta.- Al enviar un
<form method="dialog">que esté dentro, el diálogo se cierra y sureturnValuetoma el valor del botón pulsado. - Hasta 640 px de ancho, o con menos de 30 rem de alto (un teléfono en horizontal), ocupa toda la pantalla: sin margen, radio ni sombra.
- Entonces es lo único que se desplaza: la cabecera con el cierre queda arriba y el pie abajo (en una ventana baja se desplaza). El relleno respeta los márgenes de las áreas seguras.
Sin JavaScript
Todo funciona en los navegadores que admiten command. En los demás, el botón de apertura no hace nada: consulta «Problemas conocidos».
Teclado y ARIA
Tab y Mayús+Tab permiten moverse solo dentro del diálogo; Escape lo cierra.
- Al abrirlo, el foco pasa al primer elemento que lo admite o al que tenga
autofocus. - Asigna un nombre al diálogo con
aria-labelledby, que apunte a su título. - El botón × necesita
aria-label="Close dialog": su texto es un símbolo.
El contraste y el foco están en la pestaña Diseño.
Diseño
Cuándo usarlo
Usa un diálogo modal para pedir una decisión o una breve respuesta que deba resolverse antes de continuar.
- Para confirmar una acción que no se puede deshacer.
- Para pedir uno o dos datos, como un nombre nuevo.
Cuándo no usarlo
No uses un diálogo modal para algo que se pueda ignorar o retomar más tarde.
- Para información sobre la página, usa una alerta.
- Para una confirmación que no requiere respuesta, usa un aviso emergente.
- Para un formulario largo, usa una página propia.
- No abras nunca un diálogo modal al cargar la página.
Qué hacer y qué no
Cómo escribir el contenido
El título formula la pregunta o nombra la tarea: «¿Eliminar este borrador?».
- El cuerpo explica lo que ocurrirá en una o dos frases.
- El botón principal repite la acción: «Eliminar borrador». El otro dice «Cancelar».
- Nunca uses «Aceptar», «Sí» ni «Confirmar».
Contraste y foco
El texto aparece sobre la superficie del diálogo, por encima de una capa oscura que atenúa la página.
| Parte | Color sobre el fondo | Claro | Oscuro | Alto contraste | Requisitos |
|---|---|---|---|---|---|
Título y cuerpo | --luz-text-primary on --luz-surface | 16.5:1 | 14.0:1 | 21.0:1 | 4.5:1 |
Botón de cierre | --luz-text-secondary on --luz-surface | 6.4:1 | 6.9:1 | 19.3:1 | 4.5:1 |
Anillo de foco | --luz-focus-ring on --luz-surface | 5.9:1 | 7.2:1 | 21.0:1 | 3:1 |
Medido a partir de los valores de los tokens de cada modo, redondeado hacia abajo. Los requisitos son los mínimos de WCAG 2.2 AA: 4,5:1 para el texto y 3:1 para los anillos de foco, los bordes y los iconos.
- Foco: al abrirse, pasa al diálogo y, al cerrarse, vuelve al botón que lo abrió. El botón de cierre tiene un contorno
--luz-focus-ringde 2 px, a 2 px de distancia. - Bordes: el diálogo tiene un borde y una sombra, así que también queda separado de la página con colores forzados.
- Área interactiva: el botón de cierre mide al menos 28 px cuadrados, y 44 px cuando el diálogo ocupa toda la pantalla.
Tokens que usa
Los 22 tokens que lee components/modal.css, agrupados por familia. Cada familia enlaza a su página, con los valores de cada modo. En Figma son variables con los mismos nombres: consulta Tokens en Figma.
- Color
--luz-divider,--luz-focus-ring,--luz-scrim,--luz-surface,--luz-surface-hover,--luz-text-primary,--luz-text-secondary- Spacing and sizing
--luz-control-lg,--luz-control-sm,--luz-space-2xs,--luz-space-lg,--luz-space-md,--luz-space-sm,--luz-space-xl,--luz-space-xs- Typography
--luz-text-2xl,--luz-text-lg- Radius
--luz-radius-md- Shadows
--luz-shadow-lg- Motion
--luz-duration-base,--luz-duration-fast,--luz-easing-enter
El teclado y ARIA están en la pestaña Código.