Dev view

Alert

An inline non-blocking message that surfaces important information to the user — system status, validation errors, success confirmation, contextual warnings — and announces itself to assistive tech via an `aria-live` region. Distinct from Modal (blocking, demands response), Toast (auto-dismissed, transient), and Tooltip (on-demand description). Alert sits in the document flow, persists until dismissed or the underlying state changes, and may carry optional inline actions.

Also called Inline notification Callout Notice

When to use

Use

When the user must be informed of a system state, validation result, or contextual situation that does not require immediate response — form submission errors, save confirmations, network warnings, system maintenance notices. Alert sits in the document flow, persists until dismissed or the underlying state changes, and announces via aria-live.

Avoid

For decisions or destructive confirmations that block the user — that is `Modal[variant=alertdialog]`. For transient auto-dismissed feedback — that is `Toast`. For on-demand descriptive text — that is `Tooltip`. For permanent informational content (page descriptions, instructions) — that is body prose, not Alert.

Versus related

  • modal

    `Modal[variant=alertdialog]` is a blocking confirmation that demands user response (focus trap + inert siblings). `Alert` is non-blocking and inline; the user may continue working while the alert is visible. Do not confuse the terminology — "alert" the inline message vs "alert dialog" the blocking modal.

  • toast

    `Toast` auto-dismisses after a short visible duration (3–7 seconds canonical); `Alert` persists until the user dismisses or the underlying state changes. Toast is for transient confirmations ("Saved"); Alert is for situations the user may need to revisit or act on.

  • tooltip

    `Tooltip` is on-demand description revealed by hover or focus; `Alert` is system-pushed announcement. Tooltip is pulled by the user; Alert is pushed by the system.

  • banner

    `Banner` is a region-wide persistent informational surface (e.g. a maintenance notice at the top of every page); `Alert` is contextual and tied to a specific situation (a form error, a recent action confirmation). Banner often spans the viewport width; Alert sits within a region.

  • drawer

    `Drawer` is an edge-anchored panel that may be modal or non-modal and hosts arbitrary content (filters, settings, a navigation tree). `Alert` is an inline `aria-live` message announcing system state. The two surfaces only overlap if a non-modal drawer is misused as a permanent notice strip — reach for Banner or Alert there instead.

  • badge

    `Badge` is a compact inline marker (a glyph or short label) announcing metadata about a host element; `Alert` is a row- spanning inline message with body, icon, and optional actions. The decision test: does the message need its own block with prose-level content, or is it a single word / number marker on another element? Badges live next to the thing they describe; alerts own their row.

Alert is a non-modal in-page announcement that requests user attention through assistive-tech politeness rather than focus. It sits between Banner (page-level persistent strip) and Toast (transient floating queue) — alerts live inline next to the content that triggered them, dismiss only by user or programmatic action, and announce via aria-live so screen-reader users hear them without focus shift. The reference covers the four severity variants, the live-region politeness contract, and the boundary between Alert and the modal alertdialog.

Highlight
Fig 1.1 · Alert · Dev view

Implementations

How specific libraries realise the canonical anatomy. Each entry records the deltas between the canon and the library's surface.

radix Callout
import { Callout } from '@radix-ui/themes';
import { ExclamationTriangleIcon } from '@radix-ui/react-icons';
{/* Closest Radix equivalent — colour + role convey severity manually */}
<Callout.Root color="red" role="alert">
<Callout.Icon>
<ExclamationTriangleIcon />
</Callout.Icon>
<Callout.Text>
Access denied. Please contact the network administrator to view this page.
</Callout.Text>
</Callout.Root>

Divergence

From Type → To Rationale
anatomy[container] reshaped Callout.Root with explicit consumer-supplied role attribute Canonical container owns the live-region semantics: it maps severity (error/warning → role="alert", info/success → role="status") automatically through an internal severity prop. Callout.Root is an unstyled div with no awareness of severity; the consumer must pass role="alert" or role="status" as a plain HTML attribute. Without this, the Callout is silent to screen readers. Source: https://www.radix-ui.com/themes/docs/components/callout (fetched 2026-05-05), code example shows role="alert" passed manually.
anatomy[title] omitted Callout ships only two content parts: Callout.Icon and Callout.Text. There is no title part. Consumers who want a heading above the body text must compose it as free children inside Callout.Text or add a sibling element — the library provides no semantic slot or layout hook for a title. Source: https://www.radix-ui.com/themes/docs/components/callout (fetched 2026-05-05), API reference lists only Root / Icon / Text.
anatomy[icon] renamed Callout.Icon Same purpose — a size-normalised wrapper for a severity-indicating glyph at the inline-start of the callout. Radix names it Callout.Icon rather than the canonical "icon". The wrapper renders as a div; the icon glyph itself is supplied by the consumer (e.g. from @radix-ui/react-icons). Source: https://www.radix-ui.com/themes/docs/components/callout (fetched 2026-05-05).
anatomy[body] renamed Callout.Text Callout.Text is the sole text container (renders as a <p>). It serves the same role as the canonical body slot — the primary prose of the alert — but is named "Text" (matching the Radix Themes typographic primitive) rather than "body". Because there is no title part, the Callout.Text must carry both the heading-level summary and the body prose when needed. Source: https://www.radix-ui.com/themes/docs/components/callout (fetched 2026-05-05).
anatomy[action-group] omitted Callout has no action-group slot or any mechanism for embedding action buttons within the component boundary. Consumers who need inline actions must compose button elements as free siblings outside Callout.Root — there is no layout, spacing, or keyboard contract provided by the library. Source: https://www.radix-ui.com/themes/docs/components/callout (fetched 2026-05-05), API reference lists no action-related parts.
anatomy[close-button] omitted Callout ships no dismiss affordance. The component is a static, always-visible surface with no built-in dismissal state, close button, or onDismiss callback. Consumers who need dismissal must implement unmounting themselves (e.g. conditional rendering driven by local state) and compose their own accessible close button outside the Callout compound. Source: https://www.radix-ui.com/themes/docs/components/callout (fetched 2026-05-05).
axes.variants reshaped color prop on Callout.Root (blue | green | red | gray | + full Radix accent palette) Canonical severity variants (info / success / warning / error) map semantic intent to a role and a visual treatment in a single axis. Callout splits these concerns: visual treatment is driven by the `color` prop (an open accent-palette enum — blue, green, red, gray, etc.) combined with `variant` (soft | surface | outline), while live-region role is a bare HTML attribute the consumer applies manually. There is no severity enum; colour choice is purely visual and has no implied ARIA contract. Source: https://www.radix-ui.com/themes/docs/components/callout (fetched 2026-05-05), Root props table.
axes.properties[dismissible] omitted Callout has no dismissible prop, no internal dismissed state, and no onDismiss callback. The component is always visible once rendered; lifecycle management is entirely the consumer's responsibility. Source: https://www.radix-ui.com/themes/docs/components/callout (fetched 2026-05-05).
axes.properties[hasIcon] omitted There is no boolean prop to toggle the icon region. Icon presence is controlled by whether the consumer includes a Callout.Icon child element — pure composition, not a configuration prop. Source: https://www.radix-ui.com/themes/docs/components/callout (fetched 2026-05-05).
axes.properties[hasTitle] omitted Follows directly from the omission of the title part. Because there is no Callout.Title component, there is no hasTitle toggle. Source: https://www.radix-ui.com/themes/docs/components/callout (fetched 2026-05-05).
axes.properties[density] renamed size prop on Callout.Root ("1" | "2" | "3", default "2") Canonical density (comfortable / compact) controls internal spacing and padding. Callout.Root exposes a numeric size scale (1 = small, 2 = medium, 3 = large) that serves the same purpose — adjusting typographic size and internal padding — but uses ordinal numbers rather than semantic density names, and adds a third tier (large) that canonical does not model. The two axes are not directly interchangeable: size="1" approximates compact but also changes font-size, not only spacing. Source: https://www.radix-ui.com/themes/docs/components/callout (fetched 2026-05-05), Root props table, size default "2".
axes.properties[density] extended + variant prop on Callout.Root ("soft" | "surface" | "outline", default "soft") Callout ships a visual-style axis with no canonical equivalent: `variant` controls whether the callout background is a tinted fill (soft), a bordered tinted surface (surface), or an outlined frame (outline). This axis is orthogonal to severity — it expresses a visual weight preference independent of alert state. Canonical alert has no equivalent property. Source: https://www.radix-ui.com/themes/docs/components/callout (fetched 2026-05-05), Root props table.
axes.states omitted Callout is a stateless display component. It carries no open/busy/ dismissed data states, no interactive hover or focus-visible states on the container, and no state-transition graph. All canonical states (open, busy, dismissed, transitions) are absent. Source: https://www.radix-ui.com/themes/docs/components/callout (fetched 2026-05-05).
events[dismiss] omitted No dismiss event exists because the component has no dismiss mechanism. Source: https://www.radix-ui.com/themes/docs/components/callout (fetched 2026-05-05).
events[actionInvoke] omitted No action-invocation event exists because the component has no action slots. Source: https://www.radix-ui.com/themes/docs/components/callout (fetched 2026-05-05).
motion.durations omitted Callout is a static, always-rendered surface. It has no enter/exit animation and ships no motion tokens or data-state attributes for transition hooks. Source: https://www.radix-ui.com/themes/docs/components/callout (fetched 2026-05-05).
motion.reducedMotionFallback omitted With no animation to suppress, the reduced-motion contract is not applicable. Source: https://www.radix-ui.com/themes/docs/components/callout (fetched 2026-05-05).
Why this audit reads the way it does

Radix Themes Callout is the library's closest analogue to the canonical Alert — it is an inline, non-modal informational surface. However, it is a styled display primitive, not a semantically-aware alert component: severity is expressed through manual colour selection and a bare HTML role attribute, not through a library-managed severity prop. The three- part compound (Root / Icon / Text) covers the container and icon/body anatomy, but omits title, action-group, close-button, state management, motion, and any dismiss lifecycle entirely. The most consequential divergence is the missing severity-to-role mapping: consumers who reach for Callout as a drop-in Alert will silently ship a visually styled but screen-reader-invisible notification unless they remember to add role="alert" or role="status" themselves. The Radix documentation surfaces this only in a code example, not in the API reference.

Dev

Code anatomy

Slot Code slot Semantic
container container alert-or-status
icon icon presentational
title title heading-or-strong
body body prose
primary-action from action-group primary-action button
secondary-action from action-group secondary-action button
close-button from close-button close-button button
close-icon from close-button close-icon img-decorative
close-label from close-button close-label visually-hidden
Both

Variants, properties, states

Variants

Structurally different versions of the component.

info success warning aka caution · attention error aka danger · destructive · critical

Properties

The same component, parameterised.

PropertyType
dismissible boolean
hasIcon boolean
hasTitle boolean
density comfortable | compact

States

Browser/user-driven (interactive) vs. app-driven (data).

KindStates
interactive
hoverfocus-visible
data
openbusydismissed
Both

State transitions

FromToTrigger
openbusyUser activates an action (e.g. Retry). The action handler is in flight; the alert's actions show a busy indicator and become non-activatable. The body and severity remain announced.
busyopenThe action settles (success or failure). On success the consumer typically updates the alert's variant or replaces the alert with a new one; on failure the alert stays in error state with the failure cause appended.
opendismissedUser clicks the dismiss button (when `dismissible: true`). The alert is removed from the document and from the live region; focus moves to the next document focusable unless the alert was the only focusable in its container, in which case focus restores to the trigger that surfaced the alert (rare).
Dev

Cross-framework expression

FrameworkStructure mechanismVariant mechanism
Web Components A `<ui-alert>` host with named slots for `icon` / `title` / `body` / `actions`; `role` reflected from the `severity` attribute (error/warning → alert; info/success → status) attributes (`severity="error"`, `dismissible`, `density="compact"`); `data-state="open|busy|dismissed"` for CSS
React Single `<Alert>` component with `severity` prop driving role; compound subcomponents (`Alert.Icon`, `Alert.Title`, `Alert.Body`, `Alert.Actions`) or named props for content props with class-variance-authority for severity / density; `dismissible` boolean drives close-button visibility plus `onDismiss` callback
Angular (signals) `<ui-alert>` component with content projection (`<ng-content select="[ui-alert-title]">`, `<ng-content select="[ui-alert-actions]">`); host bindings drive `[attr.role]` input<'info' | 'success' | 'warning' | 'error'>(); `[dismissible]`, `[hasIcon]` host bindings; `output('dismiss')`
Vue Single `<Alert>` SFC with named slots for `icon`, `title`, `body`, `actions`; `:severity` prop drives the role defineProps with literal-union types; emits `dismiss`
Both

Events

  1. dismiss
    Payload
    `{ source: 'closeButton' | 'programmatic' }`. Distinguishes a user-clicked dismiss from a consumer-triggered programmatic removal. Programmatic removal does not re-render the alert automatically; consumers manage the alert lifecycle.
    Web Components
    `dismiss` CustomEvent on the host with `event.detail = { source }`.
    React
    `onDismiss(source)` callback. Some libraries pass the original DOM event instead of a discriminated union; consumers wrap to recover the canonical contract.
    Angular Signals
    `output<DismissSource>('dismiss')`.
    Vue
    `@dismiss` event with payload `{ source }`.
  2. actionInvoke
    Payload
    `{ actionId: string }` — the canonical id of the invoked action. The consumer is responsible for executing the action's effect (retrying a request, undoing a change). The alert transitions to `busy` state until the consumer signals completion.
    Web Components
    `actionInvoke` CustomEvent with `event.detail = { actionId }`.
    React
    `onAction(actionId)` callback or per-action `onClick` props on the action slot. The canonical event aggregates per- action handlers into one stream.
    Angular Signals
    `output<string>('actionInvoke')`.
    Vue
    `@action-invoke` event with payload `{ actionId }`.
Dev

Performance thresholds

  • stackDepthvisible-alert-count3alerts

    The canonical maximum visible alert count per region is 3. Above this threshold, visual hierarchy collapses and SR announcement floods at page load. Replace stacks of 4+ with a summary alert linking to the full list rendered as a non-live region.

Both

Accessibility

Slot Accessibility hint
container Apply `role="alert"` for assertive severities (error, warning) where the user must attend immediately, or `role="status"` for polite severities (info, success) where the user should be informed but not interrupted. Both roles imply an implicit `aria-live` region; do not stack `aria-live` attributes on top.
icon Decorative — set `aria-hidden="true"` on the icon. Severity is communicated to SR via the `role` (alert vs status) and through the title or body text, never through the icon glyph alone (icon-only severity violates WCAG 1.4.1 Use of Color and 1.3.3 Sensory Characteristics).
title Use a real heading element of an appropriate level when the alert is a top-of-region announcement (e.g. a form error summary). For inline contextual alerts, a `<strong>` or styled `<p>` is sufficient — no heading is required.
body Plain prose. The first user-facing text the SR announces when the alert mounts (after the role is announced). Avoid embedding actions inline — separate actions into the action-group region (primary-action / secondary-action slots) for keyboard reachability.
primary-action Real button with accessible name. First in DOM order; visual position is inline-end via logical properties. Action buttons receive focus via Tab in document order; the alert does not auto-focus its actions on mount (that would interrupt the user's current task).
secondary-action Real button with accessible name. Second in DOM order; visually inline-start of the primary action in LTR.
close-button Real <button type="button"> with an accessible name. Pick one of: aria-label="Close" OR a visually-hidden <span> child — declaring both is duplicative announcement. The Escape key must trigger the same action when the host surface is dismissible.
close-icon aria-hidden="true" on the SVG. Never give the icon its own accessible name (title attribute, <title> child, or aria-label) — that double-announces alongside the host's name.
close-label Visually-hidden <span> "Dismiss <severity> message" ("Dismiss info message", "Dismiss error message") inside the button, OR aria-label on the host with the same text. Activation removes the alert from the live region; SR users do not hear a secondary "alert dismissed" announcement (the silence confirms removal).
Both

Accessibility acceptance

Keyboard walk

KeysExpected
TabFocus moves through the alert in document order: actions first (in DOM order), then the dismiss button. The alert container itself is not focusable; the body is plain prose with no tabindex.
Enter or Space (focus on action button)Activates the action; alert transitions to `busy` while the consumer's handler runs. Focus stays on the action button (or moves to the dismiss button per consumer choice) after settle.
Enter or Space (focus on dismiss button)Dismisses the alert. Focus moves to the next document focusable; if the alert was the only content in its region, focus restores to the trigger that surfaced the alert (rare for inline alerts, common for transient success-after-submit).
Tab (after dismiss)Continues from where focus landed after dismissal. The dismissed alert is removed from DOM; subsequent Tab traverses remaining document focusables.

Screen-reader announcements

TriggerExpected
Alert mounts in a live region (assertive severity)SR interrupts the current announcement and reads the alert's role ("alert") followed by the title (if present) and body. Verbose modes may include the severity word ("Error:" prefix); polite-only modes hear only the body.
Alert mounts in a live region (polite severity)SR queues the announcement after the current one completes — reads the role ("status") followed by title and body. Polite severity does not interrupt.
Alert action invoked, transitions to busyAction button announces busy state via `aria-busy="true"`; SR users hear "<action label>, busy". The alert body text is not re-announced.
Alert dismissedSR users do not hear a secondary "alert dismissed" announcement; the silence (and removal of the live region content) confirms removal. Avoid synthesising a "dismissed" announcement — it adds noise without value.

axe-core rules to assert

  • aria-allowed-role
  • aria-required-attr
  • color-contrast
  • role-img-alt
  • landmark-unique
  • empty-heading

Same data as JSON for direct ingestion into Playwright + @axe-core/playwright or Jest + jest-axe: /api/components/alert/a11y-fixture.json

Both

Contracts

Vocabulary drift

APG
Alert
APG's "Alert" matches the canonical name (live-region pattern).
Carbon
Inline Notification
Polaris
Banner
Material 3
Banner
Atlassian
Notice
Several systems reserve "Alert" for the modal-confirmation pattern that this canon calls `Modal[variant=alertdialog]`; the whenToUse.vsRelated for Modal documents the distinction.
Dev

Common mistakes

Blocker

#alert-no-aria-live

Alert with no live-region announcement

Problem

The alert renders visually but the role is missing or the element is not in a live region. SR users do not hear the alert; the message is silently delivered to sighted users only.

Fix

Apply `role="alert"` for assertive severities (error, warning) or `role="status"` for polite severities (info, success). Both roles imply an implicit live region — do not stack `aria-live` on top. Test with at least one SR before shipping.

Blocker

#alert-color-only-severity

Severity differentiated by colour alone

Problem

The alert's severity is communicated only through background or border colour. Colour-vision deficient users cannot distinguish info from error; SR users get no severity cue because the role is the same.

Fix

Triple-layer severity: visual (background + border + severity icon), accessible role (alert vs status), explicit prose ("Error:" prefix or named severity in title). Each layer redundantly communicates severity.

Major

#alert-actions-inside-prose

Alert actions embedded inline in body text

Problem

Body prose contains "Click here to retry" or similar embedded interaction. The interactive element is either an anchor (wrong semantic for an action) or a styled span with a click handler (no semantic at all).

Fix

Move actions to the dedicated action-group region (primary-action / secondary-action slots) as Button elements. The body describes the situation; actions are separately reachable.

Major

#alert-dismiss-loses-issue

Dismiss removes the issue, not just the alert

Problem

The user dismisses an important warning by accident; the underlying issue (unsaved changes, expired session, network failure) silently persists. The alert was the only signal, and now the signal is gone.

Fix

Distinguish dismiss-the-alert from resolve-the-issue. For warnings of persistent system state, re-render the alert when the user next encounters the affected feature. For transient confirmations, dismissal is final by design. Document the contract per severity: error and warning alerts should not auto-disappear; info and success may.

Minor

#alert-stacked-without-summary

More than three alerts stacked without summary

Problem

A page accumulates alerts (form validation errors, system messages, user feedback). The stack grows beyond three — visual hierarchy collapses, SR announcement floods at page load, the user cannot prioritise.

Fix

Cap canonical alert stacks at three. Beyond, render a summary alert ("3 errors prevent submission") with a link to the full list rendered as a regular region (not live). Document the threshold in the performance block.

Figma↔Code mismatches
  1. 01
    Figma

    An alert drawn with severity communicated by background colour alone

    Code

    Severity is reflected via `role` (alert vs status) plus the icon glyph plus the visible severity word in the body

    Consequence

    Designers may rely on Figma's coloured background as the severity differentiator and skip the icon and the explicit severity word. Implementations following the Figma file ship colour-only severity — fails WCAG 1.4.1 (Use of Color) for users with colour-vision deficiencies, and fails for SR users entirely (the role is not implicit in colour).

    Correct

    Severity is communicated through THREE layers: visual treatment (background, border, icon glyph), accessible role (alert vs status), and explicit prose (the body or title text names the severity for SR users).

  2. 02
    Figma

    A dismissible alert drawn with no clear undo affordance

    Code

    An alert with destructive dismissal (the underlying state is also lost)

    Consequence

    Designers draw an "×" close button without considering what dismissal means. Developers ship `onDismiss` that simply removes the alert from the DOM; the user dismisses an important warning by accident, the warning is lost, and the underlying problem persists silently.

    Correct

    Distinguish dismiss-the-alert from dismiss-the-issue. Dismissing the alert is a UI affordance; dismissing the issue requires user action (Retry, Undo). Document both in the actions slot. For warnings of system state, the alert should re-appear when the underlying state next requires attention.

  3. 03
    Figma

    Multiple alerts drawn stacked at the top of a region

    Code

    Multiple alerts stacked dynamically, all wired to live regions

    Consequence

    Designers draw a stack of alerts in mocks; developers ship with each alert in its own live region. SR users hear a torrent of announcements at page load (every alert fires simultaneously); pointer users see a wall of severity treatment competing for attention.

    Correct

    Limit canonical alert stacks to 1–3. Beyond that, replace with a "summary alert" (e.g. "3 errors prevent submission") that links to the full list rendered as a non-live region. Document the canonical maximum in the performance block.

  4. 04
    Figma

    Alert actions drawn as inline links inside the body prose

    Code

    Alert actions as separate button elements in the actions slot

    Consequence

    Designers compose body text with embedded links ("Click here to retry"); developers either ship the literal anchor (link semantics for an action — see Link mistakes) or invent buttons that look like links (visual confusion). Keyboard reachability and SR announcements suffer either way.

    Correct

    Alert actions live in the dedicated actions slot as Button elements (potentially with `variant: tertiary` for visual restraint). Body prose names what the actions do ("Save failed.") without itself being interactive.