--- title: Liquid Glass Alert Dialog description: A premium 3D liquid glass modal alert dialog featuring frosted backdrop blur, spring-animated crystal glass panel, variant icon badge, liquid glass Cancel and coloured Confirm buttons, Escape key and backdrop-click dismiss, and 5 severity variants. --- ## Installation CLI Manual ```bash npx lightswind@latest add liquid-glass-alert-dialog ``` Copy and paste the following code into your project. ```tsx "use client"; import React, { useEffect, useCallback } from "react"; import { createPortal } from "react-dom"; import { cn } from "@/components/lib/utils"; import { motion, AnimatePresence } from "framer-motion"; import { AlertTriangle, Trash2, CheckCircle2, Info, Zap, X, LucideIcon, } from "lucide-react"; /* ─── Types ─────────────────────────────────────────────────────── */ export type LiquidGlassAlertDialogVariant = | "danger" | "warning" | "success" | "info" | "aurora"; export interface LiquidGlassAlertDialogProps { open: boolean; onOpenChange: (open: boolean) => void; /** Title shown in the dialog header */ title?: string; /** Descriptive body text */ description?: string; /** Override the default icon */ icon?: React.ReactNode; variant?: LiquidGlassAlertDialogVariant; /** Confirm button label */ confirmLabel?: string; /** Cancel button label */ cancelLabel?: string; /** Called when the confirm action is clicked */ onConfirm?: () => void; /** Called when the cancel action is clicked */ onCancel?: () => void; /** Extra content to render inside the dialog body */ children?: React.ReactNode; /** Close on backdrop click (default: true) */ closeOnBackdrop?: boolean; className?: string; } /* ─── Per-variant config ─────────────────────────────────────────── */ interface VariantConfig { Icon: LucideIcon; iconRing: string; iconBg: string; iconColor: string; titleColor: string; confirmBtn: string; accentGlow: string; } const VARIANTS: Record = { danger: { Icon: Trash2, iconRing: "ring-rose-400/60 dark:ring-rose-400/50", iconBg: "bg-gradient-to-b from-rose-500/25 via-red-500/15 to-rose-600/10 dark:from-rose-500/40 dark:via-rose-600/25 dark:to-rose-700/15 border border-rose-400/40 dark:border-rose-400/30", iconColor: "text-rose-600 dark:text-rose-300", titleColor: "text-rose-950 dark:text-rose-100", confirmBtn: cn( "bg-gradient-to-b from-rose-400/90 via-red-500/85 to-rose-600/90", "border border-white/60 dark:border-white/30", "shadow-[0_10px_28px_-4px_rgba(244,63,94,0.6),inset_0_2px_1.5px_rgba(255,220,220,0.95),inset_0_-3px_6px_rgba(130,15,20,0.4)]", "text-white hover:shadow-[0_14px_32px_-4px_rgba(244,63,94,0.75)]" ), accentGlow: "shadow-[0_0_80px_-10px_rgba(244,63,94,0.4)] dark:shadow-[0_0_100px_-10px_rgba(244,63,94,0.55)]", }, warning: { Icon: AlertTriangle, iconRing: "ring-amber-400/60 dark:ring-amber-400/50", iconBg: "bg-gradient-to-b from-amber-500/25 via-orange-500/15 to-amber-600/10 dark:from-amber-500/40 dark:via-amber-600/25 dark:to-amber-700/15 border border-amber-400/40 dark:border-amber-400/30", iconColor: "text-amber-600 dark:text-amber-300", titleColor: "text-amber-950 dark:text-amber-100", confirmBtn: cn( "bg-gradient-to-b from-amber-400/90 via-orange-500/85 to-amber-600/90", "border border-white/60 dark:border-white/30", "shadow-[0_10px_28px_-4px_rgba(245,158,11,0.5),inset_0_2px_1.5px_rgba(255,245,220,0.95),inset_0_-3px_6px_rgba(120,60,0,0.4)]", "text-white hover:shadow-[0_14px_32px_-4px_rgba(245,158,11,0.7)]" ), accentGlow: "shadow-[0_0_80px_-10px_rgba(245,158,11,0.35)] dark:shadow-[0_0_100px_-10px_rgba(245,158,11,0.5)]", }, success: { Icon: CheckCircle2, iconRing: "ring-emerald-400/60 dark:ring-emerald-400/50", iconBg: "bg-gradient-to-b from-emerald-500/25 via-teal-500/15 to-emerald-600/10 dark:from-emerald-500/40 dark:via-emerald-600/25 dark:to-emerald-700/15 border border-emerald-400/40 dark:border-emerald-400/30", iconColor: "text-emerald-600 dark:text-emerald-300", titleColor: "text-emerald-950 dark:text-emerald-100", confirmBtn: cn( "bg-gradient-to-b from-emerald-400/90 via-emerald-600/85 to-teal-700/90", "border border-white/60 dark:border-white/30", "shadow-[0_10px_28px_-4px_rgba(16,185,129,0.55),inset_0_2px_1.5px_rgba(220,255,235,0.95),inset_0_-3px_6px_rgba(5,80,50,0.4)]", "text-white hover:shadow-[0_14px_32px_-4px_rgba(16,185,129,0.7)]" ), accentGlow: "shadow-[0_0_80px_-10px_rgba(16,185,129,0.35)] dark:shadow-[0_0_100px_-10px_rgba(16,185,129,0.5)]", }, info: { Icon: Info, iconRing: "ring-sky-400/60 dark:ring-sky-400/50", iconBg: "bg-gradient-to-b from-sky-500/25 via-blue-500/15 to-sky-600/10 dark:from-sky-500/40 dark:via-blue-600/25 dark:to-sky-700/15 border border-sky-400/40 dark:border-sky-400/30", iconColor: "text-sky-600 dark:text-sky-300", titleColor: "text-sky-950 dark:text-sky-100", confirmBtn: cn( "bg-gradient-to-b from-sky-400/90 via-blue-500/85 to-indigo-600/90", "border border-white/60 dark:border-white/30", "shadow-[0_10px_28px_-4px_rgba(14,165,233,0.5),inset_0_2px_1.5px_rgba(220,240,255,0.95),inset_0_-3px_6px_rgba(0,60,130,0.4)]", "text-white hover:shadow-[0_14px_32px_-4px_rgba(14,165,233,0.65)]" ), accentGlow: "shadow-[0_0_80px_-10px_rgba(14,165,233,0.35)] dark:shadow-[0_0_100px_-10px_rgba(14,165,233,0.5)]", }, aurora: { Icon: Zap, iconRing: "ring-violet-400/60 dark:ring-violet-400/50", iconBg: "bg-gradient-to-b from-violet-500/25 via-fuchsia-500/15 to-violet-600/10 dark:from-violet-500/40 dark:via-fuchsia-600/25 dark:to-violet-700/15 border border-violet-400/40 dark:border-violet-400/30", iconColor: "text-violet-600 dark:text-violet-300", titleColor: "text-violet-950 dark:text-violet-100", confirmBtn: cn( "bg-gradient-to-b from-violet-400/90 via-fuchsia-500/85 to-violet-700/90", "border border-white/60 dark:border-white/30", "shadow-[0_10px_28px_-4px_rgba(139,92,246,0.5),inset_0_2px_1.5px_rgba(240,220,255,0.95),inset_0_-3px_6px_rgba(60,20,120,0.4)]", "text-white hover:shadow-[0_14px_32px_-4px_rgba(139,92,246,0.65)]" ), accentGlow: "shadow-[0_0_80px_-10px_rgba(139,92,246,0.35)] dark:shadow-[0_0_100px_-10px_rgba(139,92,246,0.5)]", }, }; /* ─── Component ─────────────────────────────────────────────────── */ export const LiquidGlassAlertDialog: React.FC = ({ open, onOpenChange, title, description, icon, variant = "danger", confirmLabel = "Confirm", cancelLabel = "Cancel", onConfirm, onCancel, children, closeOnBackdrop = true, className, }) => { const cfg = VARIANTS[variant]; const IconComp = cfg.Icon; // Close on Escape const handleKeyDown = useCallback( (e: KeyboardEvent) => { if (e.key === "Escape") onOpenChange(false); }, [onOpenChange] ); useEffect(() => { if (open) { document.addEventListener("keydown", handleKeyDown); document.body.style.overflow = "hidden"; } return () => { document.removeEventListener("keydown", handleKeyDown); document.body.style.overflow = ""; }; }, [open, handleKeyDown]); const handleConfirm = () => { onConfirm?.(); onOpenChange(false); }; const handleCancel = () => { onCancel?.(); onOpenChange(false); }; if (typeof window === "undefined") return null; return createPortal( {open && ( <> {/* ── Backdrop ─────────────────────────────────────────── */} onOpenChange(false) : undefined} className="fixed inset-0 z-[99998] backdrop-blur-md" style={{ background: "linear-gradient(135deg, rgba(15,23,42,0.65) 0%, rgba(30,0,60,0.55) 100%)", }} aria-hidden="true" /> {/* ── Dialog Panel ─────────────────────────────────────── */} e.stopPropagation()} className={cn( "fixed z-[99999] inset-0 flex items-center justify-center p-4 pointer-events-none" )} > {/* Top gloss glare — full width pill, same as button/switch */} {/* Bottom prism refraction */} {/* Close × */} onOpenChange(false)} type="button" aria-label="Close dialog" className={cn( "absolute top-4 right-4 z-20 w-8 h-8 rounded-full flex items-center justify-center transition-all duration-200 cursor-pointer", "bg-slate-900/10 dark:bg-black/40 hover:bg-slate-900/20 dark:hover:bg-black/60", "border border-slate-900/15 dark:border-white/30", "shadow-[inset_0_1.5px_1px_rgba(255,255,255,0.8)] dark:shadow-[inset_0_1.5px_1px_rgba(255,255,255,0.3)]", "text-slate-800 dark:text-white" )} > {/* ── Body ─────────────────────────────────────────── */} {/* Icon badge */} {/* Badge top gloss */} {icon ?? } {/* Title */} {title && ( {title} )} {/* Description */} {description && ( {description} )} {/* Optional slot content */} {children && ( {children} )} {/* ── Action buttons ──────────────────────────────── */} {/* Cancel — pure glass */} {/* cancel button top gloss */} {cancelLabel} {/* Confirm — variant coloured liquid glass */} {/* confirm button top gloss */} {confirmLabel} > )} , document.body ); }; LiquidGlassAlertDialog.displayName = "LiquidGlassAlertDialog"; export default LiquidGlassAlertDialog; ``` ## Usage ```tsx import { LiquidGlassAlertDialog } from "@/components/lightswind/liquid-glass-alert-dialog" ``` ```tsx import { useState } from "react"; import { LiquidGlassAlertDialog } from "@/components/lightswind/liquid-glass-alert-dialog"; import { LiquidGlassButton } from "@/components/lightswind/liquid-glass-button"; export function Demo() { const [open, setOpen] = useState(false); return ( <> setOpen(true)}> Delete Account console.log("Deleted")} /> > ); } ```
{description}