Alert Dialog
Modal dialog for critical confirmations requiring user attention and explicit action
Import
import {
AvAlertDialogImports,
AvAlertDialogService,
} from '@avesra/angular';Usage
import { Component } from '@angular/core';
import {
AvAlertDialogImports,
AvButtonComponent,
} from '@avesra/angular';
@Component({
selector: 'app-alert-dialog-basic-demo',
imports: [
AvAlertDialogImports,
AvButtonComponent,
],
template: `<av-alert-dialog>
<button av-button variant="danger" av-alert-dialog-trigger>Delete Project</button>
<ng-template avAlertDialogContent>
<div av-alert-dialog-dialog class="sm:max-w-[400px]">
<av-alert-dialog-close-trigger />
<div av-alert-dialog-header>
<div av-alert-dialog-icon status="danger"></div>
<h2 av-alert-dialog-heading>Delete project permanently?</h2>
</div>
<div av-alert-dialog-body>
<p>
This will permanently delete <strong>My Awesome Project</strong> and all of its
data. This action cannot be undone.
</p>
</div>
<div av-alert-dialog-footer>
<button av-button variant="tertiary" av-alert-dialog-close>Cancel</button>
<button av-button variant="danger" av-alert-dialog-close>Delete Project</button>
</div>
</div>
</ng-template>
</av-alert-dialog>`,
})
export class AlertDialogBasicDemo {}Anatomy
<av-alert-dialog>
<button av-button av-alert-dialog-trigger>Open Alert Dialog</button>
<ng-template avAlertDialogContent>
<div av-alert-dialog-dialog>
<!-- Optional: Close button -->
<av-alert-dialog-close-trigger />
<div av-alert-dialog-header>
<!-- Optional: Status icon -->
<div av-alert-dialog-icon></div>
<h2 av-alert-dialog-heading></h2>
</div>
<div av-alert-dialog-body></div>
<div av-alert-dialog-footer></div>
</div>
</ng-template>
</av-alert-dialog>Examples
Statuses
import { Component } from '@angular/core';
import {
AvAlertDialogImports,
AvButtonComponent,
} from '@avesra/angular';
import type { AvAlertDialogIconStatus, AvButtonVariant } from '@avesra/angular';
@Component({
selector: 'app-alert-dialog-statuses-demo',
imports: [
AvAlertDialogImports,
AvButtonComponent,
],
template: `<div class="flex flex-wrap gap-4">
@for (example of statusExamples; track example.status) {
<av-alert-dialog>
<button av-button [style]="example.triggerStyle" av-alert-dialog-trigger>
{{ example.trigger }}
</button>
<ng-template avAlertDialogContent>
<div av-alert-dialog-dialog class="sm:max-w-[400px]">
<av-alert-dialog-close-trigger />
<div av-alert-dialog-header>
<div av-alert-dialog-icon [status]="example.status"></div>
<h2 av-alert-dialog-heading>{{ example.header }}</h2>
</div>
<div av-alert-dialog-body>
<p>{{ example.body }}</p>
</div>
<div av-alert-dialog-footer>
<button av-button variant="tertiary" av-alert-dialog-close>
{{ example.cancel }}
</button>
<button
av-button
[variant]="example.confirmVariant"
av-alert-dialog-close
>
{{ example.confirm }}
</button>
</div>
</div>
</ng-template>
</av-alert-dialog>
}
</div>`,
})
export class AlertDialogStatusesDemo {
readonly statusExamples: {
status: AvAlertDialogIconStatus;
trigger: string;
header: string;
body: string;
classNames: string;
triggerStyle: Record<string, string>;
cancel: string;
confirm: string;
confirmVariant: AvButtonVariant;
}[] = [
{
status: 'accent',
trigger: 'Sign Out',
header: 'Sign out of your account?',
body: "You'll need to sign in again to access your account. Any unsaved changes will be lost.",
classNames: 'bg-accent-soft text-accent-soft-foreground',
triggerStyle: {
'--av-button-bg': 'var(--av-accent-soft)',
'--av-button-bg-hover': 'var(--av-accent-soft-hover)',
'--av-button-bg-pressed': 'var(--av-accent-soft-hover)',
'--av-button-fg': 'var(--av-accent-soft-foreground)',
},
cancel: 'Stay Signed In',
confirm: 'Sign Out',
confirmVariant: 'primary',
},
{
status: 'success',
trigger: 'Complete Task',
header: 'Complete this task?',
body: 'This will mark the task as complete and notify all team members. The task will be moved to your completed list.',
classNames: 'bg-success-soft text-success-soft-foreground',
triggerStyle: {
'--av-button-bg': 'var(--av-success-soft)',
'--av-button-bg-hover': 'var(--av-success-soft-hover)',
'--av-button-bg-pressed': 'var(--av-success-soft-hover)',
'--av-button-fg': 'var(--av-success-soft-foreground)',
},
cancel: 'Not Yet',
confirm: 'Mark Complete',
confirmVariant: 'primary',
},
{
status: 'warning',
trigger: 'Discard Changes',
header: 'Discard unsaved changes?',
body: 'You have unsaved changes that will be permanently lost. Are you sure you want to discard them?',
classNames: 'bg-warning-soft text-warning-soft-foreground',
triggerStyle: {
'--av-button-bg': 'var(--av-warning-soft)',
'--av-button-bg-hover': 'var(--av-warning-soft-hover)',
'--av-button-bg-pressed': 'var(--av-warning-soft-hover)',
'--av-button-fg': 'var(--av-warning-soft-foreground)',
},
cancel: 'Keep Editing',
confirm: 'Discard',
confirmVariant: 'primary',
},
{
status: 'danger',
trigger: 'Delete Account',
header: 'Delete your account?',
body: 'This will permanently delete your account and remove all your data from our servers. This action is irreversible.',
classNames: 'bg-danger-soft text-danger-soft-foreground',
triggerStyle: {
'--av-button-bg': 'var(--av-danger-soft)',
'--av-button-bg-hover': 'var(--av-danger-soft-hover)',
'--av-button-bg-pressed': 'var(--av-danger-soft-hover)',
'--av-button-fg': 'var(--av-danger-soft-foreground)',
},
cancel: 'Cancel',
confirm: 'Delete Account',
confirmVariant: 'danger',
},
];
}Placements
import { TitleCasePipe } from '@angular/common';
import { Component } from '@angular/core';
import {
AvAlertDialogImports,
AvButtonComponent,
} from '@avesra/angular';
import type { AvAlertDialogPlacement } from '@avesra/angular';
@Component({
selector: 'app-alert-dialog-placements-demo',
imports: [
TitleCasePipe,
AvAlertDialogImports,
AvButtonComponent,
],
template: `<div class="flex flex-wrap gap-4">
@for (placement of placements; track placement) {
<av-alert-dialog [placement]="placement">
<button av-button variant="secondary" av-alert-dialog-trigger>
{{ placement | titlecase }}
</button>
<ng-template avAlertDialogContent>
<div av-alert-dialog-dialog class="sm:max-w-[400px]">
<av-alert-dialog-close-trigger />
<div av-alert-dialog-header>
<div av-alert-dialog-icon status="accent"></div>
<h2 av-alert-dialog-heading>
{{
placement === 'auto'
? 'Auto Placement'
: (placement | titlecase) + ' Position'
}}
</h2>
</div>
<div av-alert-dialog-body>
<p>
@if (placement === 'auto') {
Automatically positions at the bottom on mobile and center on desktop for
optimal user experience.
} @else {
This dialog is positioned at the {{ placement }} of the viewport. Critical
confirmations are typically centered for maximum attention.
}
</p>
</div>
<div av-alert-dialog-footer>
<button av-button variant="tertiary" av-alert-dialog-close>Cancel</button>
<button av-button av-alert-dialog-close>Confirm</button>
</div>
</div>
</ng-template>
</av-alert-dialog>
}
</div>`,
})
export class AlertDialogPlacementsDemo {
readonly placements: readonly AvAlertDialogPlacement[] = ['auto', 'top', 'center', 'bottom'];
}Sizes
import { TitleCasePipe } from '@angular/common';
import { Component } from '@angular/core';
import {
AvAlertDialogImports,
AvButtonComponent,
} from '@avesra/angular';
import type { AvAlertDialogSize } from '@avesra/angular';
import { AppIconComponent } from '../../components/app-icon/app-icon.component';
@Component({
selector: 'app-alert-dialog-sizes-demo',
imports: [
TitleCasePipe,
AppIconComponent,
AvAlertDialogImports,
AvButtonComponent,
],
template: `<div class="flex flex-wrap gap-4">
@for (size of sizes; track size) {
<av-alert-dialog [size]="size">
<button av-button variant="secondary" av-alert-dialog-trigger>
{{ size | titlecase }}
</button>
<ng-template avAlertDialogContent>
<div av-alert-dialog-dialog>
<av-alert-dialog-close-trigger />
<div av-alert-dialog-header>
<div av-alert-dialog-icon status="default" class="bg-default text-foreground">
<app-icon icon="solar:rocket-linear" size="20" />
</div>
<h2 av-alert-dialog-heading>Size: {{ size | titlecase }}</h2>
</div>
<div av-alert-dialog-body>
<p>
@if (size === 'cover') {
This alert dialog uses the <code>cover</code> size variant. It spans the full
screen with margins: 16px on mobile and 40px on desktop. Maintains rounded
corners and standard padding. Perfect for critical confirmations that need
maximum width while preserving alert dialog aesthetics.
} @else {
This alert dialog uses the <code>{{ size }}</code> size variant. On mobile
devices, all sizes adapt to near full-width for optimal viewing. On desktop,
each size provides a different maximum width to suit various content needs.
}
</p>
</div>
<div av-alert-dialog-footer>
<button av-button variant="tertiary" av-alert-dialog-close>Cancel</button>
<button av-button av-alert-dialog-close>Confirm</button>
</div>
</div>
</ng-template>
</av-alert-dialog>
}
</div>`,
})
export class AlertDialogSizesDemo {
readonly sizes: readonly AvAlertDialogSize[] = ['xs', 'sm', 'md', 'lg', 'cover'];
}Controlled State
With signal()
Control the alert dialog using Angular's signal for simple state management. Perfect for basic use cases.
Status: closed
With open / close / toggle helpers
Wrap a signal with helper methods like open(), close(), and toggle() for a cleaner imperative API.
Status: closed
import { Component, signal } from '@angular/core';
import {
AvAlertDialogImports,
AvButtonComponent,
} from '@avesra/angular';
@Component({
selector: 'app-alert-dialog-controlled-demo',
imports: [
AvAlertDialogImports,
AvButtonComponent,
],
template: `<div class="flex max-w-md flex-col gap-8">
<div class="flex flex-col gap-3">
<h3 class="text-lg font-semibold text-foreground">With signal()</h3>
<p class="text-sm leading-relaxed text-pretty text-muted">
Control the alert dialog using Angular's <code class="text-foreground">signal</code> for
simple state management. Perfect for basic use cases.
</p>
<div class="flex flex-col items-start gap-3 rounded-2xl bg-surface p-4 shadow-sm">
<div class="flex w-full items-center justify-between">
<p class="text-xs text-muted">
Status:
<span class="font-mono font-medium text-foreground">
{{ signalOpen() ? 'open' : 'closed' }}
</span>
</p>
</div>
<div class="flex gap-2">
<button av-button size="sm" variant="secondary" (click)="openSignal()">
Open Dialog
</button>
<button av-button size="sm" variant="tertiary" (click)="toggleSignal()">
Toggle
</button>
</div>
</div>
<av-alert-dialog [(open)]="signalOpen">
<ng-template avAlertDialogContent>
<div av-alert-dialog-dialog class="sm:max-w-[400px]">
<av-alert-dialog-close-trigger />
<div av-alert-dialog-header>
<div av-alert-dialog-icon status="accent"></div>
<h2 av-alert-dialog-heading>Controlled with signal()</h2>
</div>
<div av-alert-dialog-body>
<p>
This alert dialog is controlled by Angular's <code>signal</code>. Bind
<code>[(open)]</code> to manage the dialog state externally.
</p>
</div>
<div av-alert-dialog-footer>
<button av-button variant="tertiary" av-alert-dialog-close>Cancel</button>
<button av-button av-alert-dialog-close>Confirm</button>
</div>
</div>
</ng-template>
</av-alert-dialog>
</div>
<div class="flex flex-col gap-3">
<h3 class="text-lg font-semibold text-foreground">With open / close / toggle helpers</h3>
<p class="text-sm leading-relaxed text-pretty text-muted">
Wrap a signal with helper methods like <code class="text-foreground">open()</code>,
<code>close()</code>, and <code>toggle()</code> for a cleaner imperative API.
</p>
<div class="flex flex-col items-start gap-3 rounded-2xl bg-surface p-4 shadow-sm">
<div class="flex w-full items-center justify-between">
<p class="text-xs text-muted">
Status:
<span class="font-mono font-medium text-foreground">
{{ overlayOpen() ? 'open' : 'closed' }}
</span>
</p>
</div>
<div class="flex gap-2">
<button av-button size="sm" variant="secondary" (click)="open()">Open Dialog</button>
<button av-button size="sm" variant="tertiary" (click)="toggle()">Toggle</button>
</div>
</div>
<av-alert-dialog [(open)]="overlayOpen">
<ng-template avAlertDialogContent>
<div av-alert-dialog-dialog class="sm:max-w-[400px]">
<av-alert-dialog-close-trigger />
<div av-alert-dialog-header>
<div av-alert-dialog-icon status="success"></div>
<h2 av-alert-dialog-heading>Controlled with helpers</h2>
</div>
<div av-alert-dialog-body>
<p>
Dedicated methods for common operations — call <code>open()</code>,
<code>close()</code>, or <code>toggle()</code> without hand-writing signal
updates at every call site.
</p>
</div>
<div av-alert-dialog-footer>
<button av-button variant="tertiary" av-alert-dialog-close>Cancel</button>
<button av-button av-alert-dialog-close>Confirm</button>
</div>
</div>
</ng-template>
</av-alert-dialog>
</div>
</div>`,
})
export class AlertDialogControlledDemo {
readonly signalOpen = signal(false);
readonly overlayOpen = signal(false);
openSignal(): void {
this.signalOpen.set(true);
}
toggleSignal(): void {
this.signalOpen.update((value) => !value);
}
open(): void {
this.overlayOpen.set(true);
}
close(): void {
this.overlayOpen.set(false);
}
toggle(): void {
this.overlayOpen.update((value) => !value);
}
}Custom Icon
import { Component } from '@angular/core';
import {
AvAlertDialogImports,
AvButtonComponent,
} from '@avesra/angular';
import { AppIconComponent } from '../../components/app-icon/app-icon.component';
@Component({
selector: 'app-alert-dialog-custom-icon-demo',
imports: [
AppIconComponent,
AvAlertDialogImports,
AvButtonComponent,
],
template: `<av-alert-dialog>
<button av-button variant="secondary" av-alert-dialog-trigger>Reset Password</button>
<ng-template avAlertDialogContent>
<div av-alert-dialog-dialog class="sm:max-w-[400px]">
<av-alert-dialog-close-trigger />
<div av-alert-dialog-header>
<div av-alert-dialog-icon status="warning">
<app-icon icon="solar:lock-unlocked-linear" size="20" />
</div>
<h2 av-alert-dialog-heading>Reset your password?</h2>
</div>
<div av-alert-dialog-body>
<p>
We'll send a password reset link to your email address. You'll need to create a new
password to regain access to your account.
</p>
</div>
<div av-alert-dialog-footer>
<button av-button variant="tertiary" av-alert-dialog-close>Cancel</button>
<button av-button av-alert-dialog-close>Send Reset Link</button>
</div>
</div>
</ng-template>
</av-alert-dialog>`,
})
export class AlertDialogCustomIconDemo {}Custom Trigger
Delete Item
Permanently remove this item
import { Component } from '@angular/core';
import {
AvAlertDialogImports,
AvButtonComponent,
} from '@avesra/angular';
import { AppIconComponent } from '../../components/app-icon/app-icon.component';
@Component({
selector: 'app-alert-dialog-custom-trigger-demo',
imports: [
AppIconComponent,
AvAlertDialogImports,
AvButtonComponent,
],
template: `<av-alert-dialog>
<div
av-alert-dialog-trigger
class="group flex max-w-xs items-center gap-3 rounded-2xl bg-surface p-4 shadow-sm select-none hover:bg-surface-secondary"
>
<div
class="flex size-12 shrink-0 items-center justify-center rounded-xl bg-danger-soft text-danger-soft-foreground"
>
<app-icon icon="solar:trash-bin-trash-linear" size="24" />
</div>
<div class="flex flex-1 flex-col gap-0.5">
<p class="text-sm font-semibold">Delete Item</p>
<p class="text-xs text-muted">Permanently remove this item</p>
</div>
</div>
<ng-template avAlertDialogContent>
<div av-alert-dialog-dialog class="sm:max-w-[400px]">
<av-alert-dialog-close-trigger />
<div av-alert-dialog-header>
<div av-alert-dialog-icon status="danger">
<app-icon icon="solar:trash-bin-trash-linear" size="20" />
</div>
<h2 av-alert-dialog-heading>Delete this item?</h2>
</div>
<div av-alert-dialog-body>
<p>
Use <code>av-alert-dialog-trigger</code> to create custom trigger elements beyond
standard buttons. This example shows a card-style trigger with icons and descriptive
text.
</p>
</div>
<div av-alert-dialog-footer>
<button av-button variant="tertiary" av-alert-dialog-close>Cancel</button>
<button av-button variant="danger" av-alert-dialog-close>Delete Item</button>
</div>
</div>
</ng-template>
</av-alert-dialog>`,
})
export class AlertDialogCustomTriggerDemo {}Backdrop Variants
import { TitleCasePipe } from '@angular/common';
import { Component } from '@angular/core';
import {
AvAlertDialogImports,
AvButtonComponent,
} from '@avesra/angular';
import type { AvAlertDialogBackdropVariant } from '@avesra/angular';
@Component({
selector: 'app-alert-dialog-backdrop-variants-demo',
imports: [
TitleCasePipe,
AvAlertDialogImports,
AvButtonComponent,
],
template: `<div class="flex flex-wrap gap-4">
@for (variant of backdropVariants; track variant) {
<av-alert-dialog [backdrop]="variant">
<button av-button variant="secondary" av-alert-dialog-trigger>
{{ variant | titlecase }}
</button>
<ng-template avAlertDialogContent>
<div av-alert-dialog-dialog class="sm:max-w-[400px]">
<av-alert-dialog-close-trigger />
<div av-alert-dialog-header>
<div av-alert-dialog-icon status="accent"></div>
<h2 av-alert-dialog-heading>Backdrop: {{ variant | titlecase }}</h2>
</div>
<div av-alert-dialog-body>
<p>
@switch (variant) {
@case ('opaque') {
An opaque dark backdrop that completely obscures the background, providing
maximum focus on the dialog.
}
@case ('blur') {
A blurred backdrop that softly obscures the background while maintaining
visual context.
}
@default {
A transparent backdrop that keeps the background fully visible, useful for
less critical confirmations.
}
}
</p>
</div>
<div av-alert-dialog-footer>
<button av-button variant="tertiary" av-alert-dialog-close>Cancel</button>
<button av-button av-alert-dialog-close>Confirm</button>
</div>
</div>
</ng-template>
</av-alert-dialog>
}
</div>`,
})
export class AlertDialogBackdropVariantsDemo {
readonly backdropVariants: readonly AvAlertDialogBackdropVariant[] = [
'opaque',
'blur',
'transparent',
];
}Custom Backdrop
import { Component } from '@angular/core';
import {
AvAlertDialogImports,
AvButtonComponent,
} from '@avesra/angular';
import { AppIconComponent } from '../../components/app-icon/app-icon.component';
@Component({
selector: 'app-alert-dialog-custom-backdrop-demo',
imports: [
AppIconComponent,
AvAlertDialogImports,
AvButtonComponent,
],
template: `<av-alert-dialog
backdrop="blur"
backdrop-class="bg-linear-to-t from-red-950/90 via-red-950/50 to-transparent dark:from-red-950/95 dark:via-red-950/60"
>
<button av-button variant="danger" av-alert-dialog-trigger>Delete Account</button>
<ng-template avAlertDialogContent>
<div av-alert-dialog-dialog class="sm:max-w-[420px]">
<av-alert-dialog-close-trigger />
<div av-alert-dialog-header class="items-center text-center">
<div av-alert-dialog-icon status="danger">
<app-icon icon="solar:danger-triangle-linear" size="20" />
</div>
<h2 av-alert-dialog-heading>Permanently delete your account?</h2>
</div>
<div av-alert-dialog-body>
<p>
This action cannot be undone. All your data, settings, and content will be
permanently removed from our servers. The dramatic red backdrop emphasizes the
severity and irreversibility of this decision.
</p>
</div>
<div av-alert-dialog-footer class="flex-col-reverse">
<button av-button full-width av-alert-dialog-close>Keep Account</button>
<button av-button full-width variant="danger" av-alert-dialog-close>
Delete Forever
</button>
</div>
</div>
</ng-template>
</av-alert-dialog>`,
})
export class AlertDialogCustomBackdropDemo {}Dismiss Behavior
Require explicit action (default)
Alert dialogs require explicit action by default. Backdrop click and Escape are disabled unless you opt in with dismissable and keyboard-dismiss-disabled="false".
Dismissable
For less critical confirmations, set dismissable and keyboard-dismiss-disabled="false" on av-alert-dialog.
import { Component } from '@angular/core';
import {
AvAlertDialogImports,
AvButtonComponent,
} from '@avesra/angular';
import { AppIconComponent } from '../../components/app-icon/app-icon.component';
@Component({
selector: 'app-alert-dialog-dismiss-behavior-demo',
imports: [
AppIconComponent,
AvAlertDialogImports,
AvButtonComponent,
],
template: `<div class="flex max-w-sm flex-col gap-6">
<div class="flex flex-col gap-2">
<h3 class="text-lg font-semibold">Require explicit action (default)</h3>
<p class="text-sm text-muted">
Alert dialogs require explicit action by default. Backdrop click and Escape are disabled
unless you opt in with <code>dismissable</code> and
<code>keyboard-dismiss-disabled="false"</code>.
</p>
<av-alert-dialog>
<button av-button variant="secondary" av-alert-dialog-trigger>Open Alert Dialog</button>
<ng-template avAlertDialogContent>
<div av-alert-dialog-dialog class="sm:max-w-[400px]">
<av-alert-dialog-close-trigger />
<div av-alert-dialog-header>
<div av-alert-dialog-icon status="danger">
<app-icon icon="solar:info-circle-linear" size="20" />
</div>
<h2 av-alert-dialog-heading>Action required</h2>
<p class="text-sm leading-5 text-muted">
Clicking the backdrop or pressing Escape won't close this dialog
</p>
</div>
<div av-alert-dialog-body>
<p>
Try clicking outside this alert dialog on the overlay — it won't close. You must
use the action buttons to dismiss it.
</p>
</div>
<div av-alert-dialog-footer>
<button av-button variant="tertiary" av-alert-dialog-close>Cancel</button>
<button av-button av-alert-dialog-close>Confirm</button>
</div>
</div>
</ng-template>
</av-alert-dialog>
</div>
<div class="flex flex-col gap-2">
<h3 class="text-lg font-semibold">Dismissable</h3>
<p class="text-sm text-muted">
For less critical confirmations, set <code>dismissable</code> and
<code>keyboard-dismiss-disabled="false"</code> on <code>av-alert-dialog</code>.
</p>
<av-alert-dialog [dismissable]="true" [keyboard-dismiss-disabled]="false">
<button av-button variant="secondary" av-alert-dialog-trigger>Open Alert Dialog</button>
<ng-template avAlertDialogContent>
<div av-alert-dialog-dialog class="sm:max-w-[400px]">
<av-alert-dialog-close-trigger />
<div av-alert-dialog-header>
<div av-alert-dialog-icon status="accent">
<app-icon icon="solar:info-circle-linear" size="20" />
</div>
<h2 av-alert-dialog-heading>Dismissable</h2>
<p class="text-sm leading-5 text-muted">Backdrop click and Escape work</p>
</div>
<div av-alert-dialog-body>
<p>
Click outside or press Escape to dismiss. Use this pattern for low-stakes
confirmations that don't require forced attention.
</p>
</div>
<div av-alert-dialog-footer>
<button av-button variant="tertiary" av-alert-dialog-close>Cancel</button>
<button av-button av-alert-dialog-close>Confirm</button>
</div>
</div>
</ng-template>
</av-alert-dialog>
</div>
</div>`,
})
export class AlertDialogDismissBehaviorDemo {}Close Methods
Using av-alert-dialog-close
The simplest way to close a dialog. Add av-alert-dialog-close to any button within the dialog. When clicked, it will automatically close the dialog.
Using imperative close
Drive visibility with [(open)] and call close() yourself. This gives you full control over when and how to close the dialog, allowing you to add custom logic before closing.
import { Component, signal } from '@angular/core';
import {
AvAlertDialogImports,
AvButtonComponent,
} from '@avesra/angular';
@Component({
selector: 'app-alert-dialog-close-methods-demo',
imports: [
AvAlertDialogImports,
AvButtonComponent,
],
template: `<div class="flex max-w-2xl flex-col gap-8">
<div class="flex flex-col gap-2">
<h3 class="text-lg font-semibold">Using av-alert-dialog-close</h3>
<p class="text-sm text-muted">
The simplest way to close a dialog. Add <code>av-alert-dialog-close</code> to any button
within the dialog. When clicked, it will automatically close the dialog.
</p>
<av-alert-dialog>
<button av-button variant="secondary" av-alert-dialog-trigger>Open Dialog</button>
<ng-template avAlertDialogContent>
<div av-alert-dialog-dialog class="sm:max-w-[400px]">
<div av-alert-dialog-header>
<div av-alert-dialog-icon status="accent"></div>
<h2 av-alert-dialog-heading>Using av-alert-dialog-close</h2>
</div>
<div av-alert-dialog-body>
<p>
Click either button below - both have <code>av-alert-dialog-close</code> and will
close the dialog automatically.
</p>
</div>
<div av-alert-dialog-footer>
<button av-button variant="tertiary" av-alert-dialog-close>Cancel</button>
<button av-button av-alert-dialog-close>Confirm</button>
</div>
</div>
</ng-template>
</av-alert-dialog>
</div>
<div class="flex flex-col gap-2">
<h3 class="text-lg font-semibold">Using imperative close</h3>
<p class="text-sm text-muted">
Drive visibility with <code>[(open)]</code> and call <code>close()</code> yourself. This
gives you full control over when and how to close the dialog, allowing you to add custom
logic before closing.
</p>
<av-alert-dialog [(open)]="imperativeOpen">
<button av-button variant="secondary" av-alert-dialog-trigger>Open Dialog</button>
<ng-template avAlertDialogContent>
<div av-alert-dialog-dialog class="sm:max-w-[400px]">
<div av-alert-dialog-header>
<div av-alert-dialog-icon status="success"></div>
<h2 av-alert-dialog-heading>Using imperative close</h2>
</div>
<div av-alert-dialog-body>
<p>
The buttons below call <code>close()</code> on the host. You can add validation
or other logic before setting open to <code>false</code>.
</p>
</div>
<div av-alert-dialog-footer>
<button av-button variant="tertiary" (click)="close()">Cancel</button>
<button av-button (click)="close()">Confirm</button>
</div>
</div>
</ng-template>
</av-alert-dialog>
</div>
</div>`,
})
export class AlertDialogCloseMethodsDemo {
readonly imperativeOpen = signal(false);
close(): void {
this.imperativeOpen.set(false);
}
}Programmatic
Use AvAlertDialogService to open confirmation dialogs imperatively without declaring overlay markup in your template.
Open a confirmation dialog programmatically with AvAlertDialogService.confirm().
import { Component, inject, signal } from '@angular/core';
import { AvAlertDialogService, AvButtonComponent } from '@avesra/angular';
import type { AvAlertDialogCloseResult } from '@avesra/angular';
@Component({
selector: 'app-alert-dialog-service-demo',
imports: [AvButtonComponent],
template: `<div class="flex max-w-md flex-col gap-4">
<p class="text-sm text-muted">
Open a confirmation dialog programmatically with
<code>AvAlertDialogService.confirm()</code>.
</p>
<button av-button variant="danger" (click)="confirmDelete()">Delete Project</button>
@if (lastResult() !== null) {
<p class="text-sm text-muted">
Last result:
<code class="font-mono text-foreground">{{ lastResult() }}</code>
</p>
}
</div>`,
})
export class AlertDialogServiceDemo {
private readonly alertDialog = inject(AvAlertDialogService);
readonly lastResult = signal<AvAlertDialogCloseResult | 'dismissed' | null>(null);
confirmDelete(): void {
this.alertDialog
.confirm({
title: 'Delete project permanently?',
description:
'This will permanently delete My Awesome Project and all of its data. This action cannot be undone.',
confirmText: 'Delete Project',
cancelText: 'Cancel',
status: 'danger',
})
.afterClosed()
.subscribe((result) => {
this.lastResult.set(result ?? 'dismissed');
});
}
}Custom Animations
import { Component } from '@angular/core';
import {
AvAlertDialogImports,
AvButtonComponent,
} from '@avesra/angular';
import { AppIconComponent } from '../../components/app-icon/app-icon.component';
const kinematicBackdrop = [
'data-[entering]:duration-400',
'data-[entering]:ease-[cubic-bezier(0.16,1,0.3,1)]',
'data-[exiting]:duration-200',
'data-[exiting]:ease-[cubic-bezier(0.7,0,0.84,0)]',
].join(' ');
const kinematicContainer = [
'data-[entering]:animate-in',
'data-[entering]:fade-in-0',
'data-[entering]:zoom-in-95',
'data-[entering]:duration-400',
'data-[entering]:ease-[cubic-bezier(0.16,1,0.3,1)]',
'data-[exiting]:animate-out',
'data-[exiting]:fade-out-0',
'data-[exiting]:zoom-out-95',
'data-[exiting]:duration-200',
'data-[exiting]:ease-[cubic-bezier(0.7,0,0.84,0)]',
].join(' ');
const fluidBackdrop = [
'data-[entering]:duration-500',
'data-[entering]:ease-[cubic-bezier(0.25,1,0.5,1)]',
'data-[exiting]:duration-200',
'data-[exiting]:ease-[cubic-bezier(0.5,0,0.75,0)]',
].join(' ');
const fluidContainer = [
'data-[entering]:animate-in',
'data-[entering]:fade-in-0',
'data-[entering]:slide-in-from-bottom-4',
'data-[entering]:duration-500',
'data-[entering]:ease-[cubic-bezier(0.25,1,0.5,1)]',
'data-[exiting]:animate-out',
'data-[exiting]:fade-out-0',
'data-[exiting]:slide-out-to-bottom-2',
'data-[exiting]:duration-200',
'data-[exiting]:ease-[cubic-bezier(0.5,0,0.75,0)]',
].join(' ');
@Component({
selector: 'app-alert-dialog-custom-animations-demo',
imports: [
AppIconComponent,
AvAlertDialogImports,
AvButtonComponent,
],
template: `<div class="flex flex-wrap gap-4">
<av-alert-dialog
[backdrop-class]="kinematicBackdrop"
[container-class]="kinematicContainer"
>
<button av-button variant="secondary" av-alert-dialog-trigger>Kinematic Scale</button>
<ng-template avAlertDialogContent>
<div av-alert-dialog-dialog class="sm:max-w-[400px]">
<av-alert-dialog-close-trigger />
<div av-alert-dialog-header>
<div av-alert-dialog-icon status="accent">
<app-icon icon="solar:stars-linear" size="20" />
</div>
<h2 av-alert-dialog-heading>Kinematic Scale Animation</h2>
</div>
<div av-alert-dialog-body>
<p class="mt-1">
Physics-based elastic scaling. Simulates a high-damping spring system with fast
transient response and prolonged settling time. Ideal for Alert Dialogs and Modals.
</p>
</div>
<div av-alert-dialog-footer>
<button av-button variant="tertiary" av-alert-dialog-close>Close</button>
<button av-button av-alert-dialog-close>Try Again</button>
</div>
</div>
</ng-template>
</av-alert-dialog>
<av-alert-dialog
[backdrop-class]="fluidBackdrop"
[container-class]="fluidContainer"
>
<button av-button variant="secondary" av-alert-dialog-trigger>Fluid Slide</button>
<ng-template avAlertDialogContent>
<div av-alert-dialog-dialog class="sm:max-w-[400px]">
<av-alert-dialog-close-trigger />
<div av-alert-dialog-header>
<div av-alert-dialog-icon status="accent">
<app-icon icon="solar:arrow-up-linear" size="20" />
</div>
<h2 av-alert-dialog-heading>Fluid Slide Animation</h2>
</div>
<div av-alert-dialog-body>
<p class="mt-1">
Simulates movement through a medium with fluid resistance. Eliminates mechanical
linearity for a natural, grounded feel. Perfect for Bottom Sheets or Toasts.
</p>
</div>
<div av-alert-dialog-footer>
<button av-button variant="tertiary" av-alert-dialog-close>Close</button>
<button av-button av-alert-dialog-close>Try Again</button>
</div>
</div>
</ng-template>
</av-alert-dialog>
</div>`,
})
export class AlertDialogCustomAnimationsDemo {
readonly kinematicBackdrop = kinematicBackdrop;
readonly kinematicContainer = kinematicContainer;
readonly fluidBackdrop = fluidBackdrop;
readonly fluidContainer = fluidContainer;
}Scroll Behavior
inside
Only the dialog body scrolls. Header and footer stay fixed while long content overflows inside the body.
outside
The entire dialog scrolls within the viewport when content is taller than the available space.
import { TitleCasePipe } from '@angular/common';
import { Component } from '@angular/core';
import {
AvAlertDialogImports,
AvButtonComponent,
} from '@avesra/angular';
import type { AvAlertDialogScroll } from '@avesra/angular';
import { AppIconComponent } from '../../components/app-icon/app-icon.component';
@Component({
selector: 'app-alert-dialog-scroll-behavior-demo',
imports: [
TitleCasePipe,
AppIconComponent,
AvAlertDialogImports,
AvButtonComponent,
],
template: `<div class="flex max-w-sm flex-col gap-6">
@for (mode of scrollModes; track mode.value) {
<div class="flex flex-col gap-2">
<h3 class="text-lg font-semibold">{{ mode.value }}</h3>
<p class="text-sm text-muted">{{ mode.description }}</p>
<av-alert-dialog [scroll]="mode.value">
<button av-button variant="secondary" av-alert-dialog-trigger>
Open ({{ mode.value | titlecase }})
</button>
<ng-template avAlertDialogContent>
<div av-alert-dialog-dialog class="sm:max-w-[400px]">
<av-alert-dialog-close-trigger />
<div av-alert-dialog-header>
<div av-alert-dialog-icon status="accent">
<app-icon icon="solar:info-circle-linear" size="20" />
</div>
<h2 av-alert-dialog-heading>
Scroll: {{ mode.value | titlecase }}
</h2>
<p class="text-sm leading-5 text-muted">{{ mode.hint }}</p>
</div>
<div av-alert-dialog-body>
@for (paragraph of scrollParagraphs; track paragraph) {
<p class="mb-3">
Paragraph {{ paragraph }}: Lorem ipsum dolor sit amet, consectetur adipiscing
elit. Nullam pulvinar risus non risus hendrerit venenatis. Pellentesque sit
amet hendrerit risus, sed porttitor quam.
</p>
}
</div>
<div av-alert-dialog-footer>
<button av-button variant="tertiary" av-alert-dialog-close>Cancel</button>
<button av-button av-alert-dialog-close>Confirm</button>
</div>
</div>
</ng-template>
</av-alert-dialog>
</div>
}
</div>`,
})
export class AlertDialogScrollBehaviorDemo {
readonly scrollModes: readonly {
value: AvAlertDialogScroll;
description: string;
hint: string;
}[] = [
{
value: 'inside',
description:
'Only the dialog body scrolls. Header and footer stay fixed while long content overflows inside the body.',
hint: 'Content scrolls within the body; header and footer stay put.',
},
{
value: 'outside',
description:
'The entire dialog scrolls within the viewport when content is taller than the available space.',
hint: 'The whole dialog moves with the viewport scroll.',
},
];
readonly scrollParagraphs = Array.from({ length: 24 }, (_, index) => index + 1);
}Stacked Footer Actions
import { Component } from '@angular/core';
import {
AvAlertDialogImports,
AvButtonComponent,
} from '@avesra/angular';
@Component({
selector: 'app-alert-dialog-stacked-footer-actions-demo',
imports: [
AvAlertDialogImports,
AvButtonComponent,
],
template: `<av-alert-dialog backdrop="blur">
<button av-button variant="secondary" av-alert-dialog-trigger>Premium offer</button>
<ng-template avAlertDialogContent>
<div av-alert-dialog-dialog class="sm:max-w-[360px]">
<div av-alert-dialog-header class="items-center text-center">
<div av-alert-dialog-icon status="accent">✨</div>
<h2 av-alert-dialog-heading>Upgrade to Pro</h2>
</div>
<div av-alert-dialog-body class="text-center">
<p>Unlock advanced components, themes, and priority support.</p>
</div>
<div av-alert-dialog-footer class="flex-col-reverse">
<button av-button class="w-full" av-alert-dialog-close>Upgrade now</button>
<button av-button class="w-full" variant="tertiary" av-alert-dialog-close>
Maybe later
</button>
</div>
<av-alert-dialog-close-trigger />
</div>
</ng-template>
</av-alert-dialog>`,
})
export class AlertDialogStackedFooterActionsDemo {}Customization
Tailwind CSS
import { Component } from '@angular/core';
import {
AvAlertDialogImports,
AvButtonComponent,
} from '@avesra/angular';
@Component({
selector: 'app-alert-dialog-custom-styles-demo',
imports: [
AvAlertDialogImports,
AvButtonComponent,
],
template: `<av-alert-dialog backdrop="blur">
<button av-button variant="secondary" av-alert-dialog-trigger>Sign out</button>
<ng-template avAlertDialogContent>
<div
av-alert-dialog-dialog
class="relative overflow-hidden border border-border/80 bg-surface shadow-2xl ring-1 ring-accent/10 sm:max-w-[400px] dark:border-border/90 dark:bg-surface dark:ring-accent/15"
>
<div
aria-hidden="true"
class="pointer-events-none absolute inset-x-0 top-0 h-24 bg-linear-to-b from-accent/6 to-transparent dark:from-accent/10"
></div>
<div
aria-hidden="true"
class="pointer-events-none absolute inset-x-8 top-0 h-px bg-linear-to-r from-transparent via-accent/35 to-transparent dark:via-accent/45"
></div>
<div av-alert-dialog-header class="relative">
<div av-alert-dialog-icon status="accent"></div>
<h2 av-alert-dialog-heading>Sign out of your account?</h2>
</div>
<div av-alert-dialog-body class="relative">
<p class="text-muted">
You will be signed out on this device. Unsaved work in
<strong class="text-foreground">Acme Workspace</strong> may be lost unless it was
saved to the cloud.
</p>
</div>
<div av-alert-dialog-footer>
<button av-button variant="tertiary" av-alert-dialog-close>Stay signed in</button>
<button av-button av-alert-dialog-close>Sign out</button>
</div>
</div>
</ng-template>
</av-alert-dialog>`,
})
export class AlertDialogCustomStylesDemo {}Global CSS
To customize the Alert Dialog component classes, you can use the @layer components directive. Learn more .
@layer components {
.av-alert-dialog__backdrop {
@apply bg-linear-to-br from-black/60 to-black/80;
}
.av-alert-dialog__dialog {
@apply rounded-2xl border border-danger/20 shadow-2xl;
}
.av-alert-dialog__header {
@apply gap-4;
}
.av-alert-dialog__icon {
@apply size-16;
}
.av-alert-dialog__close-trigger {
@apply rounded-full bg-white/10 hover:bg-white/20;
}
}Styling Reference
Avesra follows the BEM methodology to ensure component variants and states are reusable and easy to customize.
CSS Classes
The Alert Dialog component uses these CSS classes:
Base Classes
.av-alert-dialog__trigger— Trigger element that opens the alert dialog.av-alert-dialog__backdrop— Overlay backdrop behind the dialog.av-alert-dialog__container— Positioning wrapper with placement support.av-alert-dialog__dialog— Dialog content container.av-alert-dialog__header— Header section for icon and title.av-alert-dialog__heading— Heading text styles.av-alert-dialog__body— Main content area.av-alert-dialog__footer— Footer section for actions.av-alert-dialog__icon— Icon container with status colors.av-alert-dialog__close-trigger— Close button element
Backdrop Variants
.av-alert-dialog__backdrop--opaque— Opaque colored backdrop (default).av-alert-dialog__backdrop--blur— Blurred backdrop with glass effect.av-alert-dialog__backdrop--transparent— Transparent backdrop (no overlay)
Status Variants (Icon)
.av-alert-dialog__icon--default— Default gray status.av-alert-dialog__icon--accent— Accent blue status.av-alert-dialog__icon--success— Success green status.av-alert-dialog__icon--warning— Warning orange status.av-alert-dialog__icon--danger— Danger red status
Interactive States
The component supports these interactive states:
- Focus:
:focus-visibleor[data-focus-visible="true"]— Applied to trigger, dialog, and close button - Hover:
:hoveror[data-hovered="true"]— Applied to close button on hover - Active:
:activeor[data-pressed="true"]— Applied to close button when pressed - Entering:
[data-entering]— Applied during dialog opening animation - Exiting:
[data-exiting]— Applied during dialog closing animation - Placement:
[data-placement="*"]— Applied based on dialog position (auto, top, center, bottom)
Accessibility
Implements WAI-ARIA AlertDialog pattern :
- Focus trap: Focus locked within alert dialog
- Keyboard:ESC closes (when enabled), Tab cycles elements
- Screen readers: Proper ARIA attributes with
role="alertdialog" - Scroll lock: Body scroll disabled when open
- Required action: Defaults to requiring explicit user action (no backdrop/ESC dismiss)
API Reference
See also Modal for general-purpose overlays.
| Prop | Type | Default | Description |
|---|---|---|---|
open | boolean | false | Controls whether the alert dialog is open. Supports two-way binding with [(open)] (av-alert-dialog). |
dismissable | boolean | false | Whether clicking the backdrop closes the alert dialog (av-alert-dialog). |
keyboard-dismiss-disabled | boolean | true | Disables closing via the Escape key (av-alert-dialog). |
backdrop | 'opaque' | 'blur' | 'transparent' | 'opaque' | Backdrop visual variant (av-alert-dialog). |
backdrop-class | string | '' | Extra CSS classes merged onto the visual backdrop after the variant BEM classes (av-alert-dialog). |
container-class | string | '' | Extra CSS classes merged onto the overlay container after the BEM classes — useful for custom enter/exit motion (av-alert-dialog). |
placement | 'auto' | 'top' | 'center' | 'bottom' | 'auto' | Alert dialog position on screen (av-alert-dialog). |
scroll | 'inside' | 'outside' | 'inside' | Scroll behavior for long content (av-alert-dialog). |
size | 'xs' | 'sm' | 'md' | 'lg' | 'cover' | 'md' | Maximum width / layout preset (av-alert-dialog). |
scroll | 'inside' | 'outside' | undefined | — | Scroll behavior. Inherits from av-alert-dialog when omitted (div[av-alert-dialog-dialog]). |
size | 'xs' | 'sm' | 'md' | 'lg' | 'cover' | undefined | — | Size preset. Inherits from av-alert-dialog when omitted (div[av-alert-dialog-dialog]). |
aria-label | string | — | Accessible label when the trigger has no visible text ([av-alert-dialog-trigger]). |