Modal
Diálogo con foco protegido para una tarea que no puede esperar ni perderse.
Usar
import { Modal } from "@unozero-ai/ui/react";
<Modal></Modal>Composición
heading y description pintan la cabecera con la ✕; los hijos son el cuerpo, y las acciones van al slot footer. Ya no hay ModalHeader, ModalBody ni ModalFooter. En el pie, una sola acción rellena y la otra en ghost (la Regla de una Sola Decisión). Sin heading no hay cabecera ni ✕: entonces el cierre lo pones tú.
Va sobre el <dialog> nativo, en la top layer: el foco entra al abrir, queda atrapado, vuelve al botón que lo abrió al cerrar, y el resto de la página queda inerte. Esc, el clic fuera y la ✕ lo cierran solos y emiten close (onClose). Tú controlas open: ciérralo también en tu estado dentro de onClose, o el siguiente render lo vuelve a abrir.
Con formulario
El caso para el que existe: una tarea corta que no puede perderse a medias. Con dismissible={false} ni Esc ni un clic fuera lo cierran; solo la ✕ y tus botones. En el kit React, closeOnBackdrop={false} solo apagaba el clic fuera.
Los campos del kit entran en el FormData del <form>. El pie vive fuera del formulario, así que su botón lo envía con requestSubmit(), que valida los campos antes. Si necesitas decidir en el momento si se puede cerrar (hay cambios sin guardar), escucha onRequestClose: es cancelable. El ejemplo está en Sheet.
Tamaños
El ancho máximo va de sm (384 px) a 2xl (1024 px); por debajo de ese ancho el modal ocupa la pantalla menos un margen de 16 px. Elige el más estrecho en el que quepa el contenido sin partirlo: cada botón dice para qué sirve el suyo.
Alturas
auto se ajusta al contenido y el cuerpo hace scroll a partir del 70 % de la pantalla. tall (85 %) y full (92 %) fijan el alto: la cabecera y el pie se quedan quietos y solo se desplaza el cuerpo. Úsalos para listas largas y editores con pestañas, donde un alto que cambia haría saltar los botones. El cambio de alto ya no se anima: entra y sale con opacidad y escala, 250 ms.
- 10:53 · Tarifa Doble Estándar · 3 noches actualizadas
OK - 10:46 · Tarifa Doble Superior · 4 noches actualizadas
OK - 10:39 · Tarifa Suite Junior · 5 noches actualizadas
OK - 10:32 · Tarifa Familiar · 6 noches actualizadas
OK - 10:25 · Tarifa Doble Estándar · 7 noches actualizadas
OK - 10:18 · Tarifa Doble Superior · 3 noches actualizadas
Error - 10:11 · Tarifa Suite Junior · 4 noches actualizadas
OK - 10:04 · Tarifa Familiar · 5 noches actualizadas
OK - 09:57 · Tarifa Doble Estándar · 6 noches actualizadas
OK - 09:50 · Tarifa Doble Superior · 7 noches actualizadas
OK - 09:43 · Tarifa Suite Junior · 3 noches actualizadas
OK - 09:36 · Tarifa Familiar · 4 noches actualizadas
OK - 09:29 · Tarifa Doble Estándar · 5 noches actualizadas
OK - 09:22 · Tarifa Doble Superior · 6 noches actualizadas
OK - 09:15 · Tarifa Suite Junior · 7 noches actualizadas
OK - 09:08 · Tarifa Familiar · 3 noches actualizadas
OK - 09:01 · Tarifa Doble Estándar · 4 noches actualizadas
OK - 08:54 · Tarifa Doble Superior · 5 noches actualizadas
OK - 08:47 · Tarifa Suite Junior · 6 noches actualizadas
Error - 08:40 · Tarifa Familiar · 7 noches actualizadas
OK - 08:33 · Tarifa Doble Estándar · 3 noches actualizadas
OK - 08:26 · Tarifa Doble Superior · 4 noches actualizadas
OK - 08:19 · Tarifa Suite Junior · 5 noches actualizadas
OK - 08:12 · Tarifa Familiar · 6 noches actualizadas
OK - 08:05 · Tarifa Doble Estándar · 7 noches actualizadas
OK - 07:58 · Tarifa Doble Superior · 3 noches actualizadas
OK - 07:51 · Tarifa Suite Junior · 4 noches actualizadas
OK - 07:44 · Tarifa Familiar · 5 noches actualizadas
OK - 07:37 · Tarifa Doble Estándar · 6 noches actualizadas
OK - 07:30 · Tarifa Doble Superior · 7 noches actualizadas
OK - 07:23 · Tarifa Suite Junior · 3 noches actualizadas
OK - 07:16 · Tarifa Familiar · 4 noches actualizadas
Error - 07:09 · Tarifa Doble Estándar · 5 noches actualizadas
OK - 07:02 · Tarifa Doble Superior · 6 noches actualizadas
OK - 06:55 · Tarifa Suite Junior · 7 noches actualizadas
OK - 06:48 · Tarifa Familiar · 3 noches actualizadas
OK - 06:41 · Tarifa Doble Estándar · 4 noches actualizadas
OK - 06:34 · Tarifa Doble Superior · 5 noches actualizadas
OK - 06:27 · Tarifa Suite Junior · 6 noches actualizadas
OK - 06:20 · Tarifa Familiar · 7 noches actualizadas
OK
Cuándo no usar un modal
Un modal se lleva toda la pantalla y obliga a terminar o cancelar antes de seguir. Ese precio solo se paga cuando la tarea no puede esperar ni perderse. Si no es el caso, hay algo mejor:
- Confirmar una acción: ConfirmDialog o
confirm(), que ya traen el tono del riesgo y el botón en sólido. - Avisar de que algo salió bien: un toast. Un modal que solo dice «Guardado» es una interrupción sin tarea.
- Consultar un detalle mientras se trabaja en la lista (la ficha de una reserva, el historial de un huésped): un Sheet o una página propia, que no tapan lo que se estaba mirando.
- Editar un solo campo (renombrar una tarifa, cambiar una nota): edición en línea o un Popover.
- Un flujo de varios pasos (dar de alta un hotel, conectar el PMS): una página con
Stepper, que se puede dejar y retomar.
Nunca abras un modal desde otro modal.
API
Generada del custom-elements.json que publica @unozero-ai/ui. Las partes se estilan desde tu CSS con el nombre del elemento: uz-button::part(base).