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 (when iconName is set) plus the title message and optional description.
  • 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 for with-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 (per action.variant); with-split-buttons makes each button grow to 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 NotificationComponent renders injected components via NgComponentOutlet (which passes inputs by canonical name), NotificationPresetComponent overrides cssClass as input<string>('') without the class alias. Bind it as [cssClass] when using the <smart-notification-preset> selector directly, or just pass class on <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

InputTypeDefaultDescription
titleInputSignal<string> (required)-Headline text
descriptionInputSignal<string | undefined>-Optional supporting text
iconNameInputSignal<string | undefined>-Optional leading icon name
avatarUrlInputSignal<string | undefined>-Optional avatar image URL
actionsInputSignal<INotificationAction[]>[]Action buttons to render
dismissibleInputSignal<boolean>falseWhen true, renders a close button
optionsInputSignal<INotificationOptions | undefined>-Optional configuration (variant, ariaLive)
classInputSignal<string>''External CSS classes (alias for cssClass)

Outputs

OutputPayloadDescription
dismissedvoidEmitted when the user dismisses
actionClickINotificationActionClick ({ 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()"
        >
          &times;
        </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-live defaults to 'polite' (announced after the user finishes their current task) and can be raised to 'assertive' via options.ariaLive for time-critical messages.
  • The dismiss button carries aria-label="Close" since its visible content is the × glyph.
  • Custom implementations should preserve role="status" and aria-live to 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.