Alert Dialog
A confirmation dialog for destructive/important actions, wrapping Morphos's AlertDialog primitive.
Installation
npx kosmesis add alert-dialogpnpm dlx kosmesis add alert-dialogyarn dlx kosmesis add alert-dialogbunx kosmesis add alert-dialogInstall the following dependencies:
npm install @morphos/overlayspnpm add @morphos/overlaysyarn add @morphos/overlaysbun add @morphos/overlaysThis component builds on other Kosmesis components — install these first (via kosmesis add or this same manual process on their own pages): button.
Copy and paste the following code into your project.
import { StatelessComponent } from "@praxisjs/core";import { Component } from "@praxisjs/decorators";import type { Children } from "@praxisjs/shared";import { AlertDialogAction as MorphosAlertDialogAction, AlertDialogCancel as MorphosAlertDialogCancel, AlertDialogContent as MorphosAlertDialogContent, AlertDialogDescription as MorphosAlertDialogDescription, AlertDialogTitle as MorphosAlertDialogTitle, type AlertDialogActionProps as MorphosAlertDialogActionProps, type AlertDialogCancelProps as MorphosAlertDialogCancelProps, type AlertDialogContentProps as MorphosAlertDialogContentProps, type AlertDialogDescriptionProps as MorphosAlertDialogDescriptionProps, type AlertDialogTitleProps as MorphosAlertDialogTitleProps} from "@morphos/overlays";import { buttonVariants } from "./button";import { cn } from "@/lib/utils";/** * `AlertDialog` and `AlertDialogTrigger` are re-exported directly — see the note in `dialog.tsx` * for why: `new AlertDialog()` must produce a real instance with `.isOpen`/`.openDialog()`, which * a wrapping component class would not have. */export { AlertDialog, AlertDialogTrigger, type AlertDialogProps, type AlertDialogTriggerProps,} from "@morphos/overlays";export type AlertDialogContentProps = MorphosAlertDialogContentProps;@Component()export class AlertDialogContent extends StatelessComponent<AlertDialogContentProps> { render() { const { class: cls, ...rest } = this.props; return ( <MorphosAlertDialogContent class={cn( "fixed top-1/2 left-1/2 z-50 grid w-full max-w-[calc(100%-2rem)] -translate-x-1/2 -translate-y-1/2 gap-4 rounded-lg border bg-background p-6 shadow-lg outline-none sm:max-w-lg", "data-open:animate-in data-open:fade-in-0 data-open:zoom-in-95", cls, )} {...rest} /> ); }}export interface AlertDialogSlotProps { class?: string; children?: Children;}@Component()export class AlertDialogHeader extends StatelessComponent<AlertDialogSlotProps> { render() { const { class: cls, children } = this.props; return <div class={cn("flex flex-col gap-2 text-center sm:text-left", cls)}>{children}</div>; }}@Component()export class AlertDialogFooter extends StatelessComponent<AlertDialogSlotProps> { render() { const { class: cls, children } = this.props; return ( <div class={cn("flex flex-col-reverse gap-2 sm:flex-row sm:justify-end", cls)}>{children}</div> ); }}export type AlertDialogTitleProps = MorphosAlertDialogTitleProps;@Component()export class AlertDialogTitle extends StatelessComponent<AlertDialogTitleProps> { render() { const { class: cls, ...rest } = this.props; return <MorphosAlertDialogTitle class={cn("text-lg font-semibold", cls)} {...rest} />; }}export type AlertDialogDescriptionProps = MorphosAlertDialogDescriptionProps;@Component()export class AlertDialogDescription extends StatelessComponent<AlertDialogDescriptionProps> { render() { const { class: cls, ...rest } = this.props; return <MorphosAlertDialogDescription class={cn("text-sm text-muted-foreground", cls)} {...rest} />; }}export type AlertDialogActionProps = MorphosAlertDialogActionProps;@Component()export class AlertDialogAction extends StatelessComponent<AlertDialogActionProps> { render() { const { class: cls, ...rest } = this.props; return <MorphosAlertDialogAction class={cn(buttonVariants(), cls)} {...rest} />; }}export type AlertDialogCancelProps = MorphosAlertDialogCancelProps;@Component()export class AlertDialogCancel extends StatelessComponent<AlertDialogCancelProps> { render() { const { class: cls, ...rest } = this.props; return <MorphosAlertDialogCancel class={cn(buttonVariants({ variant: "outline" }), cls)} {...rest} />; }}Install the following dependencies:
npm install @morphos/overlays @praxisjs/csspnpm add @morphos/overlays @praxisjs/cssyarn add @morphos/overlays @praxisjs/cssbun add @morphos/overlays @praxisjs/cssThis component builds on other Kosmesis components — install these first (via kosmesis add or this same manual process on their own pages): button.
Copy and paste the following code into your project.
import { StatelessComponent } from "@praxisjs/core";import { cx, keyframes, Stylesheet, Styled, tokenVars } from "@praxisjs/css";import { Component } from "@praxisjs/decorators";import type { Children } from "@praxisjs/shared";import { AlertDialogAction as MorphosAlertDialogAction, AlertDialogCancel as MorphosAlertDialogCancel, AlertDialogContent as MorphosAlertDialogContent, AlertDialogDescription as MorphosAlertDialogDescription, AlertDialogTitle as MorphosAlertDialogTitle, type AlertDialogActionProps as MorphosAlertDialogActionProps, type AlertDialogCancelProps as MorphosAlertDialogCancelProps, type AlertDialogContentProps as MorphosAlertDialogContentProps, type AlertDialogDescriptionProps as MorphosAlertDialogDescriptionProps, type AlertDialogTitleProps as MorphosAlertDialogTitleProps} from "@morphos/overlays";import { ButtonStyles } from "./button";import { KosmesisTokens } from "@/lib/kosmesis-theme";const t = tokenVars(KosmesisTokens);const popIn = keyframes("kosmesis-pop-in", { from: { opacity: "0", transform: "translate(-50%, -50%) scale(0.95)" }, to: { opacity: "1", transform: "translate(-50%, -50%) scale(1)" },});class AlertDialogStyles extends Stylesheet { $content = this.css({ position: "fixed", top: "50%", left: "50%", zIndex: 50, display: "grid", width: "100%", maxWidth: "calc(100% - 2rem)", transform: "translate(-50%, -50%)", gap: "1rem", borderRadius: "0.5rem", border: `1px solid ${t.border}`, backgroundColor: t.background, padding: "1.5rem", boxShadow: "0 10px 15px -3px rgb(0 0 0 / 0.1)", outline: "none", }) .media("(min-width: 640px)", { maxWidth: "32rem" }) .on("&[data-open]", { animation: `${popIn} 150ms ease-out` }); $header = this.css({ display: "flex", flexDirection: "column", gap: "0.5rem", textAlign: "center" }).media( "(min-width: 640px)", { textAlign: "left" }, ); $footer = this.css({ display: "flex", flexDirection: "column-reverse", gap: "0.5rem" }).media("(min-width: 640px)", { flexDirection: "row", justifyContent: "flex-end", }); $title = this.css({ fontSize: "1.125rem", fontWeight: 600 }); $description = this.css({ fontSize: "0.875rem", color: t.mutedForeground });}/** * `AlertDialog` and `AlertDialogTrigger` are re-exported directly — see the note in `dialog.tsx` * for why: `new AlertDialog()` must produce a real instance with `.isOpen`/`.openDialog()`, which * a wrapping component class would not have. */export { AlertDialog, AlertDialogTrigger, type AlertDialogProps, type AlertDialogTriggerProps } from "@morphos/overlays";export type AlertDialogContentProps = MorphosAlertDialogContentProps;@Component()export class AlertDialogContent extends StatelessComponent<AlertDialogContentProps> { @Styled(AlertDialogStyles) $s!: AlertDialogStyles; render() { const { class: cls, ...rest } = this.props; return <MorphosAlertDialogContent class={cx(this.$s.$content, cls)} {...rest} />; }}export interface AlertDialogSlotProps { class?: string; children?: Children;}@Component()export class AlertDialogHeader extends StatelessComponent<AlertDialogSlotProps> { @Styled(AlertDialogStyles) $s!: AlertDialogStyles; render() { const { class: cls, children } = this.props; return <div class={cx(this.$s.$header, cls)}>{children}</div>; }}@Component()export class AlertDialogFooter extends StatelessComponent<AlertDialogSlotProps> { @Styled(AlertDialogStyles) $s!: AlertDialogStyles; render() { const { class: cls, children } = this.props; return <div class={cx(this.$s.$footer, cls)}>{children}</div>; }}export type AlertDialogTitleProps = MorphosAlertDialogTitleProps;@Component()export class AlertDialogTitle extends StatelessComponent<AlertDialogTitleProps> { @Styled(AlertDialogStyles) $s!: AlertDialogStyles; render() { const { class: cls, ...rest } = this.props; return <MorphosAlertDialogTitle class={cx(this.$s.$title, cls)} {...rest} />; }}export type AlertDialogDescriptionProps = MorphosAlertDialogDescriptionProps;@Component()export class AlertDialogDescription extends StatelessComponent<AlertDialogDescriptionProps> { @Styled(AlertDialogStyles) $s!: AlertDialogStyles; render() { const { class: cls, ...rest } = this.props; return <MorphosAlertDialogDescription class={cx(this.$s.$description, cls)} {...rest} />; }}export type AlertDialogActionProps = MorphosAlertDialogActionProps;@Component()export class AlertDialogAction extends StatelessComponent<AlertDialogActionProps> { @Styled(ButtonStyles) $btn!: ButtonStyles; render() { const { class: cls, ...rest } = this.props; return <MorphosAlertDialogAction class={cx(this.$btn.$root, this.$btn.$variantDefault, this.$btn.$sizeDefault, cls)} {...rest} />; }}export type AlertDialogCancelProps = MorphosAlertDialogCancelProps;@Component()export class AlertDialogCancel extends StatelessComponent<AlertDialogCancelProps> { @Styled(ButtonStyles) $btn!: ButtonStyles; render() { const { class: cls, ...rest } = this.props; return <MorphosAlertDialogCancel class={cx(this.$btn.$root, this.$btn.$variantOutline, this.$btn.$sizeDefault, cls)} {...rest} />; }}Examples
Usage
import { buttonVariants } from "@/components/ui/button";
import {
AlertDialog,
AlertDialogAction,
AlertDialogCancel,
AlertDialogContent,
AlertDialogDescription,
AlertDialogFooter,
AlertDialogHeader,
AlertDialogTitle,
AlertDialogTrigger,
} from "@/components/ui/alert-dialog";
@Component()
class DeleteAccountDialog extends StatefulComponent {
@State() alertDialog = new AlertDialog();
render() {
return (
<>
<AlertDialogTrigger alertDialog={this.alertDialog} class={buttonVariants({ variant: "destructive" })}>
Delete account
</AlertDialogTrigger>
<AlertDialogContent alertDialog={this.alertDialog} aria-labelledby="delete-title">
<AlertDialogHeader>
<AlertDialogTitle id="delete-title">Are you absolutely sure?</AlertDialogTitle>
<AlertDialogDescription>
This action cannot be undone. This will permanently delete your account.
</AlertDialogDescription>
</AlertDialogHeader>
<AlertDialogFooter>
<AlertDialogCancel alertDialog={this.alertDialog}>Cancel</AlertDialogCancel>
<AlertDialogAction alertDialog={this.alertDialog}>Continue</AlertDialogAction>
</AlertDialogFooter>
</AlertDialogContent>
</>
);
}
}Unlike Dialog, AlertDialogContent has no close (✕) button and no
closeOnOutsideClick by default — the user must explicitly choose Cancel or Continue.
Props
AlertDialog
Re-exported directly from @morphos/overlays — instantiate it in state (@State() alertDialog = new AlertDialog()) and pass it to every part below.
| Prop | Type | Default |
|---|---|---|
open | boolean | — controlled open state |
defaultOpen | boolean | — |
onOpenChange | (open: boolean) => void | — |
closeOnEscape | boolean | — |
closeOnOutsideClick | boolean | — |
AlertDialogTrigger
| Prop | Type | Default |
|---|---|---|
alertDialog | AlertDialog | — required |
AlertDialogContent
| Prop | Type | Default |
|---|---|---|
alertDialog | AlertDialog | — required |
aria-label | string | — |
aria-labelledby | string | — |
aria-describedby | string | — |
AlertDialogTitle
| Prop | Type | Default |
|---|---|---|
as | "h1" | "h2" | "h3" | "h4" | "h5" | "h6" | — |
AlertDialogAction / AlertDialogCancel
| Prop | Type | Default |
|---|---|---|
alertDialog | AlertDialog | — required |
onClick | () => void | — |
AlertDialogDescription, AlertDialogHeader, and AlertDialogFooter take no props beyond
class/children.