Alert Dialog Nuevo
Modal especializado para confirmaciones destructivas. Implementa el patrón WAI-ARIA Alert Dialog: aplica role="alertdialog" automáticamente, bloquea Escape y click en backdrop, y enfoca el botón Cancelar al abrir para prevenir confirmaciones accidentales. Para diálogos descartables convencionales, usa <hp-dialog>.
Demostración
Sin estilos (solo base.css)
Así se ve hp-alert-dialog usando únicamente @headless-primitives/utils/base.css. El focus trap, el bloqueo de ESC y el bloqueo de backdrop funcionan completamente.
Con estilos personalizados
<hp-alert-dialog>
<hp-alert-dialog-trigger>
<button class="open-alert">Eliminar elemento</button>
</hp-alert-dialog-trigger>
<hp-alert-dialog-backdrop class="backdrop"></hp-alert-dialog-backdrop>
<hp-alert-dialog-content class="content">
<hp-alert-dialog-title>Confirmar eliminación</hp-alert-dialog-title>
<hp-alert-dialog-description>
¿Estás seguro? Esta acción no se puede deshacer.
</hp-alert-dialog-description>
<div class="actions">
<hp-alert-dialog-cancel class="btn">Cancelar</hp-alert-dialog-cancel>
<hp-alert-dialog-action class="btn danger">Eliminar</hp-alert-dialog-action>
</div>
</hp-alert-dialog-content>
</hp-alert-dialog>.backdrop {
position: fixed;
inset: 0;
background: rgba(0, 0, 0, 0.5);
}
.content {
position: fixed;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
background: white;
padding: 1.5rem;
border-radius: 12px;
max-width: 400px;
width: 90%;
}
.actions {
display: flex;
justify-content: flex-end;
gap: 0.5rem;
margin-top: 1.5rem;
}
.btn.danger {
background: #dc2626;
color: white;
}Instalación
pnpm add @headless-primitives/alert-dialognpm install @headless-primitives/alert-dialogyarn add @headless-primitives/alert-dialogbun add @headless-primitives/alert-dialogFeatures
- ♿️
role="alertdialog"aplicado automáticamente — el consumidor no debe declararlo a mano. - 🔒
EscapeNO cierra — exige decisión explícita del usuario. - 🖱️ Click en backdrop NO cierra.
- 🎯 Foco inicial cae en
<hp-alert-dialog-cancel>(W3C APG). - 🎨 Sin estilos visuales (Headless) — posicionamiento via
base.css.
Anatomía
<hp-alert-dialog>
<hp-alert-dialog-trigger></hp-alert-dialog-trigger>
<hp-alert-dialog-backdrop></hp-alert-dialog-backdrop>
<hp-alert-dialog-content>
<hp-alert-dialog-title></hp-alert-dialog-title>
<hp-alert-dialog-description></hp-alert-dialog-description>
<hp-alert-dialog-cancel></hp-alert-dialog-cancel>
<hp-alert-dialog-action></hp-alert-dialog-action>
</hp-alert-dialog-content>
</hp-alert-dialog>API Reference
hp-alert-dialog
Contenedor raíz. Coordina apertura/cierre, focus trap, scroll lock y enrutamiento de eventos hp-cancel / hp-action hacia el cierre.
Eventos
| Evento | Detalle | Descripción |
|---|---|---|
hp-open | — | Se emite cuando el alert dialog se abre. |
hp-close | — | Se emite cuando el alert dialog se cierra (también al activar cancel o action). |
hp-cancel | — | Se emite cuando el usuario activa <hp-alert-dialog-cancel> (antes del cierre). |
hp-action | — | Se emite cuando el usuario activa <hp-alert-dialog-action> (antes del cierre). |
Métodos
| Método | Descripción |
|---|---|
open() | Abre el alert dialog programáticamente. |
close() | Cierra el alert dialog programáticamente. |
hp-alert-dialog-trigger
Elemento que abre el alert dialog al hacer click.
Atributos / Propiedades
| Atributo / Propiedad | Tipo | Por Defecto | Descripción |
|---|---|---|---|
disabled | boolean | false | Deshabilita el trigger. |
Atributos ARIA gestionados automáticamente
role="button"— Asignado si no se especifica.tabindex="0"— Habilitado cuando no está deshabilitado.aria-expanded—"true"cuando el alert está abierto,"false"cuando cerrado.aria-controls— ID del content cuando abierto.aria-disabled— Sincronizado con el estadodisabled.
hp-alert-dialog-content
Contenedor del contenido modal con focus trap. El componente aplica el role correcto sin que el consumidor lo declare.
Atributos ARIA gestionados automáticamente
role="alertdialog"— Aplicado siempre.aria-modal="true"— Aplicado siempre.aria-hidden—"true"cuando cerrado, ausente cuando abierto.aria-labelledby— Vinculado al ID del<hp-alert-dialog-title>si existe.aria-describedby— Vinculado al ID del<hp-alert-dialog-description>si existe.data-state—"open"|"closed".data-hp-overlay-content— Presente siempre (usado porbase.csspara posicionamiento fijo).id— Generado automáticamente si no se proporciona.
hp-alert-dialog-backdrop
Overlay visual. No registra listener de click: a diferencia de hp-dialog, el alert dialog requiere acción explícita.
Atributos ARIA gestionados automáticamente
data-hp-backdrop— Presente siempre.data-state—"open"|"closed".
hp-alert-dialog-title
Título semántico. Auto-asigna un id si no se proporciona y el content lo enlaza vía aria-labelledby.
hp-alert-dialog-description
Texto descriptivo. Auto-asigna un id si no se proporciona y el content lo enlaza vía aria-describedby.
hp-alert-dialog-cancel
Botón cancelar. Recibe el foco inicial al abrir (W3C APG: la opción más segura debe ser la predeterminada). Al activarse emite hp-cancel y dispara el cierre.
Atributos ARIA gestionados automáticamente
role="button"— Asignado si no se especifica.tabindex="0"— Siempre focusable.
hp-alert-dialog-action
Botón confirmar (acción destructiva). Al activarse emite hp-action y dispara el cierre. Escucha el evento hp-action en el root para ejecutar tu lógica antes/después del cierre.
Atributos ARIA gestionados automáticamente
role="button"— Asignado si no se especifica.tabindex="0"— Siempre focusable.
Accesibilidad
Adhiere al patrón WAI-ARIA APG para Alert Dialog.
Navegación por teclado
| Tecla | Acción |
|---|---|
Escape | NO cierra el alert dialog (requiere acción explícita). |
Tab | Navega entre elementos focusables dentro del dialog (focus trap). |
Shift + Tab | Navega en reversa dentro del focus trap. |
Enter / Space | Activa el trigger, cancel, action o cualquier botón focusable interno. |
Foco inicial
Al abrir, el foco aterriza en <hp-alert-dialog-cancel> siguiendo la recomendación W3C: prevenir confirmaciones accidentales colocando el cursor en la opción reversible/segura por defecto.
Ejemplos
Reaccionar a la confirmación antes del cierre
<hp-alert-dialog id="delete-dialog">
<hp-alert-dialog-trigger><button>Delete account</button></hp-alert-dialog-trigger>
<hp-alert-dialog-backdrop></hp-alert-dialog-backdrop>
<hp-alert-dialog-content>
<hp-alert-dialog-title>Delete account?</hp-alert-dialog-title>
<hp-alert-dialog-description>
This action permanently removes all your data.
</hp-alert-dialog-description>
<hp-alert-dialog-cancel>Cancel</hp-alert-dialog-cancel>
<hp-alert-dialog-action>Delete</hp-alert-dialog-action>
</hp-alert-dialog-content>
</hp-alert-dialog>
<script>
const root = document.getElementById("delete-dialog");
root.addEventListener("hp-action", async () => {
await fetch("/api/account", { method: "DELETE" });
// hp-close ya se disparó después de hp-action; aquí lanza navegación, toast, etc.
location.href = "/goodbye";
});
root.addEventListener("hp-cancel", () => {
console.log("User cancelled");
});
</script>Control programático
const dialog = document.querySelector("hp-alert-dialog");
dialog.open();
dialog.close();
dialog.addEventListener("hp-open", () => console.log("opened"));
dialog.addEventListener("hp-close", () => console.log("closed"));