Advanced Components

Alert

A composable inline message for status, warnings, and errors. Every slot is optional, so the same primitive covers a single line of text and a full block with an icon, a description, and actions.

Example

An alert using the full anatomy, with a dismiss button wired up.

Basic alert
Weekly report is ready
The usage summary for last week has been generated and sent to your inbox.
<!-- In the component class -->
private readonly dismissed = signal(false);

<!-- In the template -->
@if (!dismissed()) {
<div buiAlert [(dismissed)]="dismissed">
<mat-icon buiAlertIcon svgIcon="info" />
<div buiAlertContent>
<div buiAlertTitle>Weekly report is ready</div>
<div buiAlertDescription>
The usage summary for last week has been generated and sent to your inbox.
</div>
<div buiAlertActions>
<button matButton>Open report</button>
<button matButton>Email settings</button>
</div>
</div>
<button matIconButton buiAlertDismissButton>
<mat-icon svgIcon="x" />
</button>
</div>
}

Import

The directives are standalone. Import only the slots the alert actually uses.

import {
BuiAlert,
BuiAlertActions,
BuiAlertContent,
BuiAlertDescription,
BuiAlertDismiss,
BuiAlertDismissButton,
BuiAlertIcon,
BuiAlertTitle,
} from '@/bui/ui/alert';

Structure

The alert is fully directive-based. You compose it by applying attribute directives to standard HTML elements. Nothing is automatic: the alert has no timers, no queue, and no self removal. It renders what you give it and reports a dismiss request back to you.

<div buiAlert>
<mat-icon buiAlertIcon svgIcon="info" />

<div buiAlertContent>
<div buiAlertTitle>Title</div>
<div buiAlertDescription>Description</div>

<div buiAlertActions>
<button matButton>Action</button>
</div>
</div>

<button matIconButton buiAlertDismissButton>
<mat-icon svgIcon="x" />
</button>
</div>

The building blocks are:

DirectiveDescription
buiAlertRoot container. Holds the appearance, tone, and state.
buiAlertIconLeading icon, aligned with the first line of text.
buiAlertContentText column holding the title, description, and actions.
buiAlertTitleTitle line. Labels the alert for screen readers.
buiAlertDescriptionSupporting text. Describes the alert for screen readers.
buiAlertActionsButton row, either inside the content or beside it.
buiAlertDismissBehavior only. Dismisses the alert when clicked.
buiAlertDismissButtonDismiss behavior plus the corner icon button treatment.

Every slot is optional. A title on its own is a valid alert, so is a description on its own, and so is an icon with a title and a single action.

buiAlert

The root directive. Apply it to any element to create an alert instance. It uses role="status", wires aria-labelledby and aria-describedby to whichever of the title and description slots are present, and exposes data-state, data-appearance, and data-tone for CSS hooks.

<!-- Default (outline, neutral) -->
<div buiAlert>...</div>

<!-- Soft error -->
<div buiAlert appearance="soft" tone="error">...</div>

<!-- Two-way binding -->
<div buiAlert [(dismissed)]="isDismissed">...</div>

<!-- Template reference -->
<div buiAlert #ref="buiAlert">...</div>
PropTypeDefaultDescription
dismissedmodel<boolean>falseTwo-way binding for the dismissed state.
appearance'soft' | 'outline''outline'Surface treatment of the alert.
tone'neutral' | 'info' | 'success' | 'warning' | 'error' | 'magic''neutral'Color family of the alert.
classClassValue'' Classes merged onto the root. Available on every slot directive.

Appearance and tone

Styling runs on two independent axes. appearance decides the surface: outline is a bordered box, tinted on the colored tones and transparent on neutral, while soft is a borderless filled block. tone decides the color family and applies to the border, background, text, and icon at once. The default combination is outline and neutral.

magic is the violet tone for AI related messages, pairing with the wand-sparkles icon.

Appearance and tone combinations
Outline neutral is the default
Nothing to set. This is what a bare alert looks like.
Scheduled maintenance
The reporting dashboard will be unavailable on Sunday between 02:00 and 04:00 UTC.
Deployment complete
Version 2.15.0 is now live in the production environment.
Storage almost full
You have used 92% of the storage on your current plan. Remove unused files or upgrade to keep uploading.
Payment failed
The card on file was declined. Update your billing details to keep the subscription active.
Ask AI is ready for this workspace
Summarize logs, draft replies and search across your projects with the built in assistant.
<!-- Outline neutral (default) -->
<div buiAlert>...</div>

<!-- Soft info -->
<div buiAlert appearance="soft" tone="info">...</div>

<!-- Soft success -->
<div buiAlert appearance="soft" tone="success">...</div>

<!-- Outline warning -->
<div buiAlert tone="warning">...</div>

<!-- Outline error -->
<div buiAlert tone="error">...</div>

<!-- Soft magic -->
<div buiAlert appearance="soft" tone="magic">...</div>

The two axes cover the common cases, they are not a ceiling. Classes you pass to class are merged last on every slot, so a single instance can drop the border, swap the radius, or repaint the surface without leaving the primitive behind.

<!-- Borderless, for an alert sitting inside a card -->
<div buiAlert class="border-0">...</div>

<!-- Squared off and tighter -->
<div buiAlert class="rounded-none px-3 py-2">...</div>

<!-- Repainted surface -->
<div buiAlert appearance="soft" tone="info" class="bg-indigo-a3 text-indigo-a12">...</div>

Icon

The buiAlertIcon selector is scoped to mat-icon, so it only applies to a Material icon. It sizes the icon, inherits the tone color, and nudges it down to sit on the first line of text.

<div buiAlert tone="warning">
<mat-icon buiAlertIcon svgIcon="triangle-alert" />
<div buiAlertContent>
<div buiAlertTitle>Storage almost full</div>
</div>
</div>

Content, title and description

buiAlertContent is the text column. It takes the remaining width beside the icon and stacks the title, description, and stacked actions.

buiAlertTitle and buiAlertDescription are not just typography. The root watches for them and points aria-labelledby at the title and aria-describedby at the description, so an alert with only a description is still announced correctly.

<!-- Title only -->
<div buiAlert>
<div buiAlertContent>
<div buiAlertTitle>Your changes have been saved</div>
</div>
</div>

<!-- Description only -->
<div buiAlert>
<div buiAlertContent>
<div buiAlertDescription>
Two of your team members are still waiting for an invitation.
</div>
</div>
</div>

The description accepts paragraphs. Wrap the text in p elements and the spacing between them is handled for you.

<div buiAlertDescription>
<p>
This workspace still runs on the legacy data region. Migrations run in the
background and take about ten minutes.
</p>
<p>
API clients keep working without changes, only the webhook delivery region
moves.
</p>
</div>

Actions

buiAlertActions works in two placements. Inside buiAlertContent the buttons stack under the text and pick up a top margin. As a sibling of the content they sit beside it, trailing the alert and aligned with its first line.

Stacked actions
Verify your domain
Outgoing email from example.com stays paused until the domain records are verified.
Trailing actions
A new version is available
<!-- Stacked under the text -->
<div buiAlert tone="warning">
<mat-icon buiAlertIcon svgIcon="triangle-alert" />
<div buiAlertContent>
<div buiAlertTitle>Verify your domain</div>
<div buiAlertDescription>
Outgoing email from example.com stays paused until the domain records are verified.
</div>
<div buiAlertActions>
<button matButton>Verify domain</button>
<button matButton>Learn more</button>
</div>
</div>
</div>

<!-- Trailing the content -->
<div buiAlert>
<mat-icon buiAlertIcon svgIcon="info" />
<div buiAlertContent>
<div buiAlertTitle>A new version is available</div>
</div>
<div buiAlertActions>
<button matButton>Install now</button>
<button matButton>Release notes</button>
</div>
</div>

Buttons inside the actions slot are pinned to the small size whatever size classes they carry. An alert is a secondary surface and its buttons stay quiet, so the sizing is fixed rather than configurable.

Dismiss

buiAlertDismiss calls dismiss() on the root, which sets the dismissed model to true. That is all it does. The alert does not remove itself, so bind [(dismissed)] to a signal and control the rendering with an @if.

It carries no styling, so it fits any control that already reads as a dismiss action, a text button in the actions row for instance. buiAlertDismissButton composes that same behavior and adds the corner icon button treatment on top.

Dismiss button in the corner
Workspace settings moved
General, members, and billing now live under the workspace menu.
Dismiss as a text action
Backup completed
Last night's snapshot of the Northwind workspace finished without errors.
<!-- In the component class -->
private readonly dismissed = signal(false);

<!-- Dismiss button in the corner -->
@if (!dismissed()) {
<div buiAlert appearance="soft" tone="info" [(dismissed)]="dismissed">
<mat-icon buiAlertIcon svgIcon="info" />
<div buiAlertContent>
<div buiAlertTitle>Workspace settings moved</div>
<div buiAlertDescription>
General, members, and billing now live under the workspace menu.
</div>
</div>
<button matIconButton buiAlertDismissButton>
<mat-icon svgIcon="x" />
</button>
</div>
}

<!-- Dismiss as a text action -->
@if (!dismissed()) {
<div buiAlert tone="success" [(dismissed)]="dismissed">
<mat-icon buiAlertIcon svgIcon="circle-check" />
<div buiAlertContent>
<div buiAlertTitle>Backup completed</div>
<div buiAlertDescription>
Last night's snapshot of the Northwind workspace finished without
errors.
</div>
<div buiAlertActions>
<button matButton>View details</button>
<button matButton buiAlertDismiss>Dismiss</button>
</div>
</div>
</div>
}

buiAlertDismissButton is pinned to the top end corner at a fixed 28px, and the root reserves that corner with padding whenever one is a direct child. Both are deliberate: the corner is the one position that stays correct for a single line and for a paragraph, and a dismiss control that grows with the alert reads as a primary action. It labels itself with aria-label="Dismiss", which you can replace by setting the attribute yourself.

A text Dismiss button inside buiAlertActions takes plain buiAlertDismiss instead. It flows with the other buttons and already labels itself.

The root also carries data-state, which is open or dismissed. Use it when you want to animate the alert out or keep it in the DOM instead of removing it.

<div buiAlert class="data-[state=dismissed]:hidden" [(dismissed)]="dismissed">
...
</div>

See it in blocks

  • Outline runs the same notices as bordered alerts.
  • Soft stacks the tinted tones as a set.
  • With actions places buttons under the description and beside a single line.
  • Dismissible wires the dismissed model to an @if with a restore button.
  • Customized overrides the surface with a solid and a gradient recipe.