Dialog Diálogo
Introducción
Una ventana modal superpuesta sobre la página que requiere interacción del usuario antes de continuar.
Hidratación
Nivel 1 — auto-interactivo por defecto. Un Dialog no tiene un respaldo estático significativo —la apertura, la trampa de foco y el manejo de ESC requieren todos JavaScript del lado del cliente— por lo que se hidrata como isla por defecto. Pasa interactive={false} para renderizar una carcasa de diálogo estática e inerte que no envía JS de cliente.
Prop interactive | Resultado |
|---|---|
| omitido | Se hidrata como isla (por defecto) |
true | Se hidrata como isla |
false | Estático — sin JS de cliente |
Todas las decisiones de interactividad en la librería pasan por el helper compartido shouldHydrate() en app/components/ui/island-utils.ts.
Accesibilidad
Cumple con el patrón de diseño WAI-ARIA Dialog (Modal). Cuando está hidratado (interactive por defecto es true), el diálogo ofrece el siguiente comportamiento:
- El foco se mueve dentro del diálogo al abrir — a
initialFocusEl(), si no al primer elemento enfocable, si no al contenido en sí. - El foco queda atrapado mientras está abierto —
Tab/Shift+Tabciclan solo dentro del contenido del diálogo. Los diálogos anidados se gestionan: solo el diálogo más superior atrapa y poseeEscape. Escapecierra el diálogo (a menos quecloseOnEscape={false}).- El fondo es inerte — todo fuera del diálogo (incluido un diálogo padre detrás de uno anidado) recibe
inerty se elimina del orden de tabulación, mientras que el subárbol del diálogo permanece interactivo. - El scroll del body se bloquea mientras al menos un diálogo está abierto, y se restaura cuando se cierra el último.
- El foco vuelve al disparador al cerrar (o a
finalFocusEl()si se proporciona). - Nombre accesible — derivado de
title(aria-labelledby) o unaria-labelexplícito. Se emite unconsole.warndel lado del cliente si ninguno de los dos está presente. role="alertdialog"— pasarole="alertdialog"para confirmaciones destructivas, y apuntainitialFocusEla la acción de cancelar/segura para que reciba el foco primero. ConcloseOnInteractOutside={false}el diálogo solo se puede cerrar mediante un botón oEscape.
Uso
Diálogo básico
Confirm action
Confirm action
import { Dialog, Button } from "../components/ui";
export default function MyPage() {
return (
<Dialog
trigger={<Button>Open Dialog</Button>}
title="Confirm action"
description="Are you sure you want to continue?"
body="This action cannot be undone."
cancel={<Button variant="outline">Cancel</Button>}
confirm={<Button>Confirm</Button>}
/>
);
}
Constructor de páginas CMS
Este componente está disponible como un bloque dialog en el Constructor de páginas (content/pages/*.json). trigger es una lista de bloques anidada (el CMS siempre envía un array):
{
"type": "dialog",
"title": "Confirm action",
"description": "Are you sure you want to continue?",
"confirmText": "Confirm",
"cancelText": "Cancel",
"trigger": [{ "type": "button", "text": "Open Dialog" }]
}
Propiedades
Dialog
| Prop | Tipo | Descripción |
|---|---|---|
trigger | JSX.Element | Elemento que abre el diálogo al activarse. |
| title | `string \ | JSX.Element` | El título del diálogo. |
| description | `string \ | JSX.Element` | La descripción del diálogo. |
| body | `string \ | JSX.Element` | El contenido principal. |
| footer | `string \ | JSX.Element` | Contenido personalizado del pie. |
| cancel | JSX.Element | Elemento renderizado como disparador de cierre (cancelar). |
| confirm | JSX.Element | Elemento renderizado como disparador de acción. |
| closable | boolean | Si se muestra el botón de cierre. Por defecto: true. |
| interactive | boolean | Habilita la hidratación del lado del cliente para comportamiento interactivo. |
| class | string | Clases CSS personalizadas para el elemento raíz. |
| role | `"dialog" \ | "alertdialog"` | Variante del diálogo. Usa "alertdialog" para confirmaciones destructivas. Por defecto: "dialog". |
| aria-label | string | Nombre accesible cuando no se proporciona title. |
| closeOnEscape | boolean | Cierra al pulsar Escape. Por defecto: true. |
| closeOnInteractOutside | boolean | Cierra al hacer clic en el fondo. Por defecto: true. |
| initialFocusEl | `() => HTMLElement \ | null` | Elemento a enfocar al abrir. Por defecto el primer elemento enfocable. |
| finalFocusEl | `() => HTMLElement \ | null` | Elemento a enfocar al cerrar. Por defecto el disparador. |
Los props adicionales (por ejemplo, open, defaultOpen, onOpenChange, id) se reenvían al primitive de diálogo subyacente.