Alert
An inline, persistent message — severity palette, three appearances, actions and dismiss, in React (@bpdm/ui) and Angular (@bpdm/ng).
The Alert is an inline, persistent message — a coloured accent, a tinted
icon, a title and body, with optional actions and a dismiss button. It comes in
the full severity palette across three appearances. For transient pop-up
notifications use Toast instead. In Angular it's
<bpdm-alert>.
Usage
Approaching seat limit
import { Alert } from '@bpdm/ui/alert';
export function AlertUsage() {
return (
<Alert variant="warning" title="Approaching seat limit">
You've used 9 of 10 seats. Add more to keep inviting teammates.
</Alert>
);
}import { Component } from '@angular/core';
import { BpdmAlert } from '@bpdm/ng';
@Component({
selector: 'alert-usage',
imports: [BpdmAlert],
templateUrl: './alert-usage.html',
})
export class AlertUsage {}<bpdm-alert variant="warning" title="Approaching seat limit">
You've used 9 of 10 seats. Add more to keep inviting teammates.
</bpdm-alert>Variants
The variant prop sets the severity — default, primary, info, success,
warning, error, help, and contrast — each with a matching leading icon.
Scheduled maintenance
Deployment complete
Approaching seat limit
Build failed
checkout.spec.ts.Heads up
import { Alert } from '@bpdm/ui/alert';
export function AlertVariants() {
return (
<div className="flex flex-col gap-3">
<Alert variant="info" title="Scheduled maintenance">
We'll be down for ~10 minutes at 02:00 UTC.
</Alert>
<Alert variant="success" title="Deployment complete">
v2.4.0 is live in production.
</Alert>
<Alert variant="warning" title="Approaching seat limit">
You've used 9 of 10 seats.
</Alert>
<Alert variant="error" title="Build failed">
3 tests failed in checkout.spec.ts.
</Alert>
<Alert variant="default" title="Heads up">
This workspace switches to the new editor next week.
</Alert>
</div>
);
}import { Component } from '@angular/core';
import { BpdmAlert } from '@bpdm/ng';
@Component({
selector: 'alert-variants',
imports: [BpdmAlert],
templateUrl: './alert-variants.html',
})
export class AlertVariants {}<bpdm-alert variant="info" title="Scheduled maintenance">
We'll be down for ~10 minutes at 02:00 UTC.
</bpdm-alert>
<bpdm-alert variant="success" title="Deployment complete">v2.4.0 is live in production.</bpdm-alert>
<bpdm-alert variant="warning" title="Approaching seat limit">You've used 9 of 10 seats.</bpdm-alert>
<bpdm-alert variant="error" title="Build failed">3 tests failed in checkout.spec.ts.</bpdm-alert>
<bpdm-alert variant="default" title="Heads up">This workspace switches to the new editor next week.</bpdm-alert>Appearances
The appearance prop styles each variant — soft (tinted with an accent bar,
default), solid (filled), and outline (bordered).
Soft (default)
Solid
Outline
import { Alert } from '@bpdm/ui/alert';
export function AlertAppearances() {
return (
<div className="flex flex-col gap-3">
<Alert variant="info" appearance="soft" title="Soft (default)">
A tinted surface with a coloured accent bar.
</Alert>
<Alert variant="success" appearance="solid" title="Solid">
A filled, high-emphasis banner.
</Alert>
<Alert variant="error" appearance="outline" title="Outline">
A bordered, low-emphasis variant.
</Alert>
</div>
);
}import { Component } from '@angular/core';
import { BpdmAlert } from '@bpdm/ng';
@Component({
selector: 'alert-appearances',
imports: [BpdmAlert],
templateUrl: './alert-appearances.html',
})
export class AlertAppearances {}<bpdm-alert variant="info" appearance="soft" title="Soft (default)">
A tinted surface with a coloured accent bar.
</bpdm-alert>
<bpdm-alert variant="success" appearance="solid" title="Solid">A filled, high-emphasis banner.</bpdm-alert>
<bpdm-alert variant="error" appearance="outline" title="Outline">A bordered, low-emphasis variant.</bpdm-alert>With actions
Pass action (React) — buttons or links shown under the body. In Angular,
project them into the [bpdmAlertActions] slot.
Approaching seat limit
import { Alert } from '@bpdm/ui/alert';
import { Button } from '@bpdm/ui/button';
export function AlertWithActions() {
return (
<Alert
variant="warning"
title="Approaching seat limit"
action={
<>
<Button size="sm" variant="primary">Upgrade plan</Button>
<Button size="sm" variant="secondary" appearance="ghost">Manage members</Button>
</>
}
>
You've used 9 of 10 seats.
</Alert>
);
}import { Component } from '@angular/core';
import { BpdmAlert, BpdmAlertActions, BpdmButton } from '@bpdm/ng';
@Component({
selector: 'alert-with-actions',
imports: [BpdmAlert, BpdmAlertActions, BpdmButton],
templateUrl: './alert-actions.html',
})
export class AlertWithActions {}<bpdm-alert variant="warning" title="Approaching seat limit">
You've used 9 of 10 seats.
<div bpdmAlertActions>
<button bpdmButton size="sm" variant="primary">Upgrade plan</button>
<button bpdmButton size="sm" variant="secondary" appearance="ghost">Manage members</button>
</div>
</bpdm-alert>Dismissible
Pass onClose (React) to show a dismiss button — the alert collapses and fades,
then fires the callback. In Angular, set dismissible and handle (closed).
Invite sent
import { useState } from 'react';
import { Alert } from '@bpdm/ui/alert';
import { Button } from '@bpdm/ui/button';
export function DismissibleAlert() {
const [open, setOpen] = useState(true);
if (!open) {
return (
<Button variant="secondary" appearance="ghost" onClick={() => setOpen(true)}>
Show alert again
</Button>
);
}
return (
<Alert variant="success" title="Invite sent" onClose={() => setOpen(false)}>
Mara will get an email with a link to join.
</Alert>
);
}import { Component, signal } from '@angular/core';
import { BpdmAlert } from '@bpdm/ng';
@Component({
selector: 'dismissible-alert',
imports: [BpdmAlert],
templateUrl: './alert-dismissible.html',
})
export class DismissibleAlert {
readonly open = signal(true);
}@if (open()) {
<bpdm-alert variant="success" title="Invite sent" dismissible (closed)="open.set(false)">
Mara will get an email with a link to join.
</bpdm-alert>
}Title only
Omit the body for a compact, single-line alert.
Your changes have been saved.
import { Alert } from '@bpdm/ui/alert';
export function TitleOnly() {
return <Alert variant="info" title="Your changes have been saved." />;
}import { Component } from '@angular/core';
import { BpdmAlert } from '@bpdm/ng';
@Component({
selector: 'title-only',
imports: [BpdmAlert],
templateUrl: './alert-title-only.html',
})
export class TitleOnly {}<bpdm-alert variant="info" title="Your changes have been saved." />No icon
Hide the leading icon with icon={null} (React) / [showIcon]="false" (Angular).
Release notes
import { Alert } from '@bpdm/ui/alert';
export function NoIcon() {
return (
<Alert variant="default" icon={null} title="Release notes">
We've shipped a faster editor and a few bug fixes.
</Alert>
);
}import { Component } from '@angular/core';
import { BpdmAlert } from '@bpdm/ng';
@Component({
selector: 'no-icon',
imports: [BpdmAlert],
templateUrl: './alert-no-icon.html',
})
export class NoIcon {}<bpdm-alert variant="default" [showIcon]="false" title="Release notes">
We've shipped a faster editor and a few bug fixes.
</bpdm-alert>Internationalization
The Alert has almost no built-in copy of its own — the title, the body, and any
action labels are all author-supplied, so you translate them in your own app
exactly like the rest of the content you pass in. The one string the component
owns is the dismiss (X) button's accessible name, set via a single messages
object — a Partial<AlertMessages> merged over the English default:
-
dismiss— the dismiss (X) button's accessible name (aria-label). Default"Dismiss". -
React — pass
messagesto<Alert messages={{ dismiss: '…' }} />. -
Angular — bind
[messages]on<bpdm-alert>.
The live prop is localisation-adjacent: it controls how assertively a screen
reader announces the alert — "assertive" (the default, role="alert", right for
errors and warnings), "polite" (role="status", announced when the reader is
idle, better for non-urgent info/success), or "off" (no live role). It changes
announcement timing, not any text.
The Alert mirrors under dir="rtl" with no extra prop — the dismiss (X) button
moves to the inline-start corner and the reserved padding flips with it, while the
icon and text order follow the writing direction.
The example below localises only the built-in dismiss label to German via
messages — the alert's own title and body stay in your app's content:
import { useState } from 'react';
import { Alert, type AlertMessages } from '@bpdm/ui/alert';
// Only the built-in dismiss label is translated; your own copy stays as-is.
const messages: Partial<AlertMessages> = {
dismiss: 'Schließen',
};
export function InviteAlert() {
const [open, setOpen] = useState(true);
if (!open) return null;
return (
<Alert
variant="success"
title="Invite sent"
messages={messages}
onClose={() => setOpen(false)}
>
Mara will get an email with a link to join.
</Alert>
);
}import { Component, signal } from '@angular/core';
import { BpdmAlert, type AlertMessages } from '@bpdm/ng';
@Component({
selector: 'invite-alert',
imports: [BpdmAlert],
templateUrl: './alert-i18n.html',
})
export class InviteAlert {
readonly open = signal(true);
// Only the built-in dismiss label is translated; your own copy stays as-is.
readonly messages: Partial<AlertMessages> = {
dismiss: 'Schließen',
};
}@if (open()) {
<bpdm-alert
variant="success"
title="Invite sent"
[messages]="messages"
dismissible
(closed)="open.set(false)"
>
Mara will get an email with a link to join.
</bpdm-alert>
}API
| Prop | Type | Default | Description |
|---|---|---|---|
variant | default | primary | info | success | warning | error | help | contrast | default | Severity / colour + the default leading icon. |
appearance | soft | solid | outline | soft | Tinted-with-accent, filled, or bordered. |
title | ReactNode (Angular: string) | — | Bold heading line. |
icon | ReactNode | null | per variant | React — override the icon, or null to hide it. Angular toggles with [showIcon]. |
showIcon | boolean | true | Angular — show/hide the leading icon. |
action | ReactNode | — | React — buttons/links under the body. Angular projects [bpdmAlertActions]. |
onClose | () => void | — | React — show a dismiss button; fires after the collapse. Angular uses dismissible + (closed). |
dismissible | boolean | false | Angular — show the dismiss button (emits closed). |
messages | Partial<AlertMessages> | English | Override the translatable strings — currently just the dismiss button's accessible name (see below). Angular: [messages]. |
live | assertive | polite | off | assertive | Live-region politeness — assertive (role="alert"), polite (role="status"), or off (no live role). Angular: [live]. |
The body is children (React) / projected content (Angular). In React all native
<div> attributes are forwarded onto the root. In Angular the actions row is the
[bpdmAlertActions] directive, and (closed) is the output that fires once a
dismissible alert has finished collapsing.
App-wide copy — messages
Localise the one built-in string — the dismiss button's accessible name — in a
single place (see Internationalization). It's a
Partial<AlertMessages> merged over the English default.
- React — the
messagesprop on<Alert>. - Angular — the
[messages]input on<bpdm-alert>.
AlertMessages key | Default | Description |
|---|---|---|
dismiss | Dismiss | Accessible name (aria-label) for the dismiss (X) button. |
Accessibility
- The Alert is a live region: by default the container carries
role="alert", so assistive tech announces it assertively the moment it appears or its text changes — which suits errors and warnings. - For non-urgent info or success, set
live="polite"(role="status") so the message is announced when the screen reader is idle instead of interrupting; uselive="off"to drop the live role entirely. - The leading icon is decorative (
aria-hidden) — severity is conveyed by the visibletitleand body text, never by colour or the icon alone, so the message reads clearly on its own. - The dismiss control is a real
<button>with a visible keyboard focus ring and an accessible name frommessages.dismiss(default"Dismiss"). - Action buttons are author-supplied — pass real
button/ link elements (e.g. Button) so they stay fully keyboard-operable and focusable. - Everything mirrors under
dir="rtl"— the dismiss (X) button moves to the inline-start corner and the icon / text order follows the writing direction. - For frequent, transient feedback prefer Toast, whose live-region timing suits ephemeral messages better than a persistent alert.