Skip to main content

Alert

An alert is a message that stays in the page, next to what it is about. Each tone is a 10% tint and a 30% border of its hue under text-foreground, so a brand only sets --success, --warning and --danger for alerts to follow.

import Alert from '@zuilib/primitives/alert'

Tones

info tints from --primary (--info is a surface token, not a hue); neutral is the muted surface. Each has a default status icon, painted in the tone colour.

Loading example

Icon and actions

icon replaces the default glyph; false removes the icon column. Alert.Actions is a row for buttons under the text.

Loading example

Dismissible

dismissible adds a close button named Close, <title>. Uncontrolled, a click calls onOpenChange(false) and removes the alert (remount with a new key, or use defaultOpen, to show it again). With open, the click only calls onOpenChange(false) and the parent decides.

Loading example

Props

AlertProps extends div props (minus title).

PropTypeDefaultDescription
tone'info' | 'success' | 'warning' | 'danger' | 'neutral''info'
iconReactNode | falsetone iconfalse or null removes the icon column; decorative (aria-hidden)
politeness'assertive' | 'polite' | 'off''assertive'assertive is role="alert"; polite is role="status" for info / success / neutral (warning and danger stay alert); off renders no live role. An explicit role prop wins
dismissiblebooleanfalseRenders the close button
openbooleanControlled visibility; when given the close button only calls onOpenChange(false)
defaultOpenbooleantrueUncontrolled initial visibility
onOpenChange(open: boolean) => voidCalled with false when the close button is pressed
closeLabelstring'Close'Close button name, composed with the Alert.Title text; the default is Labels.close
classNamestringMerged last, after the tone classes

Alert.Title, Alert.Description and Alert.Actions take div props. The title is a div, not a heading; pass role="heading" aria-level={n} when it belongs in the outline. Its id (generated unless you pass one) is what the close button's name refers to.

Slots

SlotElementNotes
[data-slot="alert"]divRoot. Also data-tone, data-politeness, data-dismissible
[data-slot="alert-icon"]spanIcon column; aria-hidden
[data-slot="alert-content"]divColumn holding the children
[data-slot="alert-title"]div
[data-slot="alert-description"]divLinks inside are underlined
[data-slot="alert-actions"]divFlex row, wraps
[data-slot="alert-close"]buttonPresent when dismissible
[data-slot="alert-close-icon"]svg
/* Consumer CSS: informational alerts in a hue of your own */
[data-slot="alert"][data-tone="info"] {
background-color: color-mix(in oklch, var(--brand-blue) 10%, transparent);
border-color: color-mix(in oklch, var(--brand-blue) 30%, transparent);
}

Tokens

TokenUsed for
--primary, --success, --warning, --dangerTint and border per tone
--success-text, --warning-textIcon colour where the base hue is too light on the tint
--muted, --border, --muted-foregroundThe neutral surface and icon
--foregroundTitle and description text
--radius-lgCorner radius
--ringClose button focus ring

Accessibility

  • role="alert" by default (screen readers interrupt); politeness="polite" downgrades non-urgent tones to role="status"; politeness="off" for a callout that is part of the page on load.
  • The icon is decorative; Alert.Title carries the meaning.
  • The close button is named "{closeLabel}, {title}" through aria-labelledby, so several alerts' close buttons are distinguishable.
  • When the alert disappears while focus is inside it, focus moves to the element it came from, else the next tabbable element after the alert, then the previous one, then a focusable ancestor. It never lands on <body>.
  • Description text keeps full --foreground colour: --muted-foreground sits under 4.5:1 on the tints.
  • FieldError: the inline validation line under a field.
  • Badge: shares the -text contrast twins.