Alert
Displays a callout that draws the user's attention.
- v1.0.0
- SSR
- Zoneless
- 0.8 kB gzip
import { Component } from '@angular/core';
import { NdIcon } from '@ndop/core';
import { Alert, AlertDescription, AlertTitle } from '@/ui/alert/alert';
/** A success message with an icon, shown with the page. */
@Component({
selector: 'example-alert-demo',
imports: [Alert, AlertDescription, AlertTitle, NdIcon],
template: `
<div ndAlert role="none" class="max-w-md">
<svg ndIcon="circle-check"></svg>
<div ndAlertTitle>Your changes have been saved</div>
<div ndAlertDescription>
Everyone on the team sees the new settings right away.
</div>
</div>
`,
})
export class AlertDemoExample {}
An alert is a callout that draws attention to a message: a warning, an
error, a confirmation. It has an optional icon, a title and a
description, laid out in a grid. It's announced with the alert role by
default, which interrupts screen readers; set role="status" for news
that can wait, or role="none" for a callout that's part of the page.
Installation
npx @ndop/cli add alertpnpm dlx @ndop/cli add alertyarn dlx @ndop/cli add alertbunx @ndop/cli add alertInstall the dependencies
npm install @ndop/corepnpm add @ndop/coreyarn add @ndop/corebun add @ndop/coreCopy the code into your project
At the paths your
ndop.jsonaliases point to; these use the defaults.src/app/ui/alert/alert.tsimport { computed, Directive, input } from '@angular/core'; import { cn, variants, type VariantOf } from '@/lib/utils'; /** * Class table for {@link Alert}. */ export const alertVariants = variants({ base: 'relative grid w-full grid-cols-[0_1fr] items-start gap-y-0.5 rounded-lg border px-4 py-3 text-sm has-[>svg]:grid-cols-[calc(var(--spacing)*4)_1fr] has-[>svg]:gap-x-3 [&>svg]:size-4 [&>svg]:translate-y-0.5 [&>svg]:text-current', variants: { variant: { default: 'bg-card text-card-foreground', // The description is full destructive: shadcn's 90% is 4.49:1 on // the card, under WCAG's 4.5:1. destructive: 'bg-card text-destructive *:data-[slot=alert-description]:text-destructive [&>svg]:text-current', }, }, defaults: { variant: 'default' }, }); /** Visual style of an {@link Alert}. */ export type AlertVariant = VariantOf<typeof alertVariants, 'variant'>; /** * A callout: a message that draws attention, with an optional icon, * title and description. It's announced as an `alert` by default; use * `role="status"` for messages that can wait, or `role="none"` for ones * that are part of the page from the start. * * @example * ```html * <div ndAlert variant="destructive"> * <svg ndIcon="circle-alert"></svg> * <div ndAlertTitle>Payment failed</div> * <div ndAlertDescription>Check your card details and try again.</div> * </div> * ``` */ @Directive({ selector: '[ndAlert]', host: { 'data-slot': 'alert', '[attr.role]': 'role()', '[attr.data-variant]': 'variant()', '[class]': 'classes()', }, }) export class Alert { /** Visual style. */ readonly variant = input<AlertVariant>('default'); /** * How assistive technology treats it: `alert` interrupts, `status` * waits for a pause, `none` is read with the page. */ readonly role = input<'alert' | 'status' | 'none'>('alert'); /** Extra classes, merged last so they win. */ readonly class = input(''); protected readonly classes = computed(() => cn(alertVariants({ variant: this.variant() }), this.class()), ); } /** * An alert's title. * * @example * ```html * <div ndAlertTitle>Payment failed</div> * ``` */ @Directive({ selector: '[ndAlertTitle]', host: { 'data-slot': 'alert-title', '[class]': 'classes()' }, }) export class AlertTitle { /** Extra classes, merged last so they win. */ readonly class = input(''); protected readonly classes = computed(() => cn( 'col-start-2 line-clamp-1 min-h-4 font-medium tracking-tight', this.class(), ), ); } /** * An alert's description, under its title. * * @example * ```html * <div ndAlertDescription>Check your card details and try again.</div> * ``` */ @Directive({ selector: '[ndAlertDescription]', host: { 'data-slot': 'alert-description', '[class]': 'classes()' }, }) export class AlertDescription { /** Extra classes, merged last so they win. */ readonly class = input(''); protected readonly classes = computed(() => cn( 'col-start-2 grid justify-items-start gap-1 text-sm text-muted-foreground [&_p]:leading-relaxed', this.class(), ), ); }src/app/lib/utils.ts/** * Class helpers used by every styled item. They come from `@ndop/core`; * this file is yours, so it is the place to customize them, for example by * wrapping `cn` with an extended tailwind-merge configuration for your own * utilities. * * @packageDocumentation */ export { cn, variants, type ClassValue, type VariantOf } from '@ndop/core';Update the import paths to match your project
The files import each other through the
@/uiand@/libaliases.
Usage
import { Alert, AlertDescription, AlertTitle } from '@/ui/alert/alert';<div ndAlert role="none" class="max-w-md">
<svg ndIcon="circle-check"></svg>
<div ndAlertTitle>Your changes have been saved</div>
<div ndAlertDescription>
Everyone on the team sees the new settings right away.
</div>
</div>Examples
Alert destructive
An error, with what to do about it.
Please check your billing information and try again.
- Check your card details
- Make sure there are enough funds
- Verify the billing address
import { Component } from '@angular/core';
import { NdIcon } from '@ndop/core';
import { Alert, AlertDescription, AlertTitle } from '@/ui/alert/alert';
/** An error, with what to do about it. */
@Component({
selector: 'example-alert-destructive',
imports: [Alert, AlertDescription, AlertTitle, NdIcon],
template: `
<div ndAlert variant="destructive" role="none" class="max-w-md">
<svg ndIcon="circle-alert"></svg>
<div ndAlertTitle>Unable to process your payment</div>
<div ndAlertDescription>
<p>Please check your billing information and try again.</p>
<ul class="list-inside list-disc text-sm">
<li>Check your card details</li>
<li>Make sure there are enough funds</li>
<li>Verify the billing address</li>
</ul>
</div>
</div>
`,
})
export class AlertDestructiveExample {}
Accessibility
Alert has no keyboard interaction or focus handling of its own; it keeps the semantics of the element it's on.
API reference
Alert
[ndAlert]A callout: a message that draws attention, with an optional icon,
title and description. It's announced as an alert by default; use
role="status" for messages that can wait, or role="none" for ones
that are part of the page from the start.
| Alert binding | Type | Default |
|---|---|---|
[variant]Visual style. | 'default' | 'destructive' | 'default' |
[role]How assistive technology treats it: alert interrupts, status
waits for a pause, none is read with the page. | 'alert' | 'status' | 'none' | 'alert' |
[class]Extra classes, merged last so they win. | string | '' |
| Data attribute | Values |
|---|---|
data-slot | alert |
data-variant | — |
AlertTitle
[ndAlertTitle]An alert's title.
| AlertTitle binding | Type | Default |
|---|---|---|
[class]Extra classes, merged last so they win. | string | '' |
| Data attribute | Values |
|---|---|
data-slot | alert-title |
AlertDescription
[ndAlertDescription]An alert's description, under its title.
| AlertDescription binding | Type | Default |
|---|---|---|
[class]Extra classes, merged last so they win. | string | '' |
| Data attribute | Values |
|---|---|
data-slot | alert-description |
Agent notes
Do
- Write a short title that says what happened, and a description that says what to do.
- Use
variant="destructive"for errors, and say it in the text too. - Use
role="none"for alerts rendered with the page, and keepalertfor ones that appear after something the user did.
Don't
- Don't use an alert for a notification that should disappear on its own; use a toast.
- Don't put several
role="alert"messages on a page at once.