Notification
The <smart-notification> component displays a transient or persistent message — title, optional description, optional icon/avatar, optional dismiss control, and zero or more action buttons. It follows the Base + Standard + Wrapper pattern with an InjectionToken-based extension mechanism. The abstract NotificationBaseComponent defines the shared API — title (required), description, iconName, avatarUrl, actions, dismissible, options, cssClass (alias class), plus dismissed and actionClick outputs and the dismiss() / invokeAction() helper methods. NotificationStandardComponent is a barebones placeholder concrete implementation. NotificationComponent is the public wrapper that renders NotificationStandardComponent by default and accepts a custom replacement via NOTIFICATION_STANDARD_COMPONENT_TOKEN.
Usage
@if (visible()) {
<smart-notification
title="Successfully saved!"
description="Anyone with a link can now view this file."
[actions]="actions"
[dismissible]="true"
[options]="options"
(actionClick)="onActionClick($event)"
(dismissed)="onDismissed()"
/>
}
Components
NotificationComponent (<smart-notification>)
Main wrapper component. Renders NotificationStandardComponent by default. When NOTIFICATION_STANDARD_COMPONENT_TOKEN is provided, renders the injected component via NgComponentOutlet.
NotificationStandardComponent (<smart-notification-standard>)
Barebones placeholder concrete implementation. Renders a <div role="status"> with aria-live (defaulting to polite, overridable via options.ariaLive), an <h3> for the title, an optional <p> for the description, an optional close button when dismissible is true, and one <button> per action with a data-variant attribute. It does not include Tailwind UI styling — it exists solely as the default structural placeholder until a custom implementation is registered through the token.
NotificationPresetComponent (<smart-notification-preset>)
Styled toast variation that extends NotificationBaseComponent and is a drop-in replacement for NotificationStandardComponent. Register it via NOTIFICATION_STANDARD_COMPONENT_TOKEN to restyle every <smart-notification>, or use the <smart-notification-preset> selector directly. It renders the translated Preline toast looks as a card (bg-white/dark:bg-gray-800, border, rounded-xl, shadow-lg), with the layout selected through options.variant (default 'simple'):
simple— leading icon glyph (wheniconNameis set) plus thetitlemessage and optionaldescription.condensed— inline single-row message with the action links and close button pushed to the end (ms-auto).with-actions-below/with-avatar— icon (or avatar image forwith-avatar) + heading + description, with text-link styled actions below; the close button is absolutely positioned.with-buttons-below/with-split-buttons— same layout but actions render as solid/bordered buttons (peraction.variant);with-split-buttonsmakes each buttongrowto fill the row.
dismissible renders a close button that calls dismiss(); each action calls invokeAction(action.id). Open/close visibility is Angular-driven via the dismissed output — no Preline JS runtime is used. All classes are smart:-prefixed Tailwind with explicit dark: variants. The class recipes live in preset/preset-classes.util.ts (kept out of the public barrel; e.g. getNotificationContainerClasses, getNotificationActionClasses).
Because
NotificationComponentrenders injected components viaNgComponentOutlet(which passes inputs by canonical name),NotificationPresetComponentoverridescssClassasinput<string>('')without theclassalias. Bind it as[cssClass]when using the<smart-notification-preset>selector directly, or just passclasson<smart-notification>(the wrapper forwards it).
NotificationBaseComponent (abstract)
Abstract base directive for extending custom notification implementations. Exposes the inputs/outputs listed below and provides dismiss() (emits dismissed) and invokeAction(actionId) (emits actionClick with an INotificationActionClick payload { actionId }, exported from @smartsoft001/angular) helper methods.
API
Inputs
| Input | Type | Default | Description |
|---|---|---|---|
title | InputSignal<string> (required) | - | Headline text |
description | InputSignal<string | undefined> | - | Optional supporting text |
iconName | InputSignal<string | undefined> | - | Optional leading icon name |
avatarUrl | InputSignal<string | undefined> | - | Optional avatar image URL |
actions | InputSignal<INotificationAction[]> | [] | Action buttons to render |
dismissible | InputSignal<boolean> | false | When true, renders a close button |
options | InputSignal<INotificationOptions | undefined> | - | Optional configuration (variant, ariaLive) |
class | InputSignal<string> | '' | External CSS classes (alias for cssClass) |
Outputs
| Output | Payload | Description |
|---|---|---|
dismissed | void | Emitted when the user dismisses |
actionClick | INotificationActionClick ({ actionId: string }) | Emitted when an action button is clicked |
INotificationAction
id is echoed back in the actionClick payload so the host can route handling. variant controls the placeholder's data-variant attribute and is intended as a styling hook for custom implementations.
INotificationOptions
The standard component only consumes ariaLive (placeholder behavior — applied directly as the aria-live attribute on the role="status" container; defaults to 'polite'). The variant property is ignored by NotificationStandardComponent but fully consumed by NotificationPresetComponent (default 'simple') to pick the toast layout.
NOTIFICATION_STANDARD_COMPONENT_TOKEN
InjectionToken that allows replacing the default NotificationStandardComponent with a custom implementation. Provide a Type<NotificationBaseComponent> to override.
Extending the base class
import {
ChangeDetectionStrategy,
Component,
computed,
input,
ViewEncapsulation,
} from '@angular/core';
import {
INotificationAction,
INotificationOptions,
NOTIFICATION_STANDARD_COMPONENT_TOKEN,
NotificationBaseComponent,
NotificationComponent,
} from '@smartsoft001/angular';
/**
* A custom notification built on `NotificationBaseComponent`.
*
* The base contributes every input plus the `dismiss()` and
* `invokeAction(id)` helpers, which already emit the `dismissed` and
* `actionClick` outputs - the implementation only decides the markup.
*/
@Component({
selector: 'docs-custom-notification',
template: `
<section
role="status"
[attr.aria-live]="options()?.ariaLive ?? 'polite'"
[class]="containerClasses()"
>
@if (avatarUrl()) {
<img class="docs-notification__avatar" [src]="avatarUrl()" alt="" />
} @else if (iconName()) {
<span class="docs-notification__icon" aria-hidden="true">{{
iconName()
}}</span>
}
<div class="docs-notification__body">
<h3 class="docs-notification__title">{{ title() }}</h3>
@if (description()) {
<p class="docs-notification__description">{{ description() }}</p>
}
@if (actions().length) {
<div class="docs-notification__actions">
@for (action of actions(); track action.id) {
<button
type="button"
class="docs-notification__action"
[attr.data-variant]="action.variant ?? 'primary'"
(click)="invokeAction(action.id)"
>
{{ action.label }}
</button>
}
</div>
}
</div>
@if (dismissible()) {
<button
type="button"
class="docs-notification__dismiss"
aria-label="Close"
(click)="dismiss()"
>
×
</button>
}
</section>
`,
encapsulation: ViewEncapsulation.None,
changeDetection: ChangeDetectionStrategy.OnPush,
})
export class CustomNotificationComponent extends NotificationBaseComponent {
// NgComponentOutlet passes 'cssClass' by canonical name, not the 'class' alias.
override cssClass = input<string>('');
containerClasses = computed(() => {
const classes = ['docs-notification'];
const variant = this.options()?.variant;
if (variant) classes.push(`docs-notification--${variant}`);
const extra = this.cssClass();
if (extra) classes.push(extra);
return classes.join(' ');
});
}
/**
* Registering the implementation against
* `NOTIFICATION_STANDARD_COMPONENT_TOKEN` makes every `<smart-notification>`
* in this injector render it instead of the standard variation.
*
* NgComponentOutlet does not forward outputs, so the wrapper's `(dismissed)`
* and `(actionClick)` stay silent - a consumer listens on the custom component.
*/
@Component({
selector: 'docs-notification-custom-example',
changeDetection: ChangeDetectionStrategy.OnPush,
imports: [NotificationComponent],
providers: [
{
provide: NOTIFICATION_STANDARD_COMPONENT_TOKEN,
useValue: CustomNotificationComponent,
},
],
template: `
<smart-notification
title="App notifications"
description="Notifications may include alerts, sounds and icon badges."
[avatarUrl]="avatarUrl"
[actions]="actions"
[dismissible]="true"
[options]="options"
/>
`,
})
export class NotificationCustomExampleComponent {
avatarUrl =
'https://images.unsplash.com/photo-1568602471122-7832951cc4c5?auto=format&fit=facearea&facepad=2&w=300&h=300&q=80';
actions: INotificationAction[] = [
{ id: 'deny', label: "Don't allow", variant: 'secondary' },
{ id: 'allow', label: 'Allow', variant: 'primary' },
];
options: INotificationOptions = {
variant: 'with-actions-below',
ariaLive: 'polite',
};
}
Accessibility
- The container uses
role="status"so assistive technologies announce it as a live region. aria-livedefaults to'polite'(announced after the user finishes their current task) and can be raised to'assertive'viaoptions.ariaLivefor time-critical messages.- The dismiss button carries
aria-label="Close"since its visible content is the×glyph. - Custom implementations should preserve
role="status"andaria-liveto keep this contract.
Source
The component lives in packages/shared/angular/src/lib/components/notification and is documented for Claude Code by the angular-components-notification skill.