Skip to content

Feedback

Alert and Callout

One surface, two intents. An Alert reports a status about something that happened; a Callout explains something persistent. Neither is announced to assistive technology unless you ask for it.

Examples

Five tones

A surface, a semantic edge and a semantic icon — never a flooded panel. These blocks sit beside tables and charts, and a wall of colour destroys the hierarchy.

Nothing to report

All scheduled jobs finished within their window.

Backfill queued

1,284 bars will be ingested for BTCUSDT 1h.

Validation passed

Seven analytics domains matched their expected row counts.

Delayed data

Yahoo returned end-of-day prices for the last two sessions.

One surface, two intents

`emphasis` is the only difference between the two exports: an Alert is a status about something that happened, a Callout is context that stays. They look alike on purpose — the difference is what the reader is meant to do about it.

Alert · a status

Warm-up period

Sharpe is not meaningful before 60 bars of history.

Callout · persistent context

This benchmark comes from an external provider

Provider data may be delayed or end-of-day depending on the market and your data agreement.

Composing the parts

`title` and `description` are shorthand for the common case; the parts compose the same block when it has to hold a link, a list or a custom mark. `icon={null}` drops the mark without dropping the tone.

Rebalance in progress

Orders are queued against the execution policy; nothing is sent until you confirm.

Signed with your own key

The mark is yours to choose; the tone still comes from the alert.

Actions and dismissal

Both are opt-in. The component never persists a dismissal, and it never hides an action behind the close button — a reader who dismisses twice should not lose their way back.

Market widget unavailable

TradingView could not be reached. The rest of this page is unaffected.

What the close button means

A dismissal is a UI event the caller hears about — persist it, log it, or reopen the alert. The component itself keeps no state, so an alert that returns on the next render is a decision the product made.

External resources

This page loads TradingView widgets at runtime.

When it is announced

`live` is the whole accessibility decision, and `off` is the default: a block that is present when the page loads is already part of the page, and announcing it as an interruption is worse than saying nothing. Press the button — the alert that arrives afterwards announces itself once.

live="polite" → role="status"; live="assertive" → role="alert"; off → no role.

Nothing is announced yet — this text is the page, not a message.

Next to a form

A page-level Alert and a form's own error summary answer different questions; they can sit together without repeating each other. The field keeps its own error — that is where the reader is looking.

Server-side validation also runs on submit

Client rules are a courtesy; the stored dataset is validated again.

Lowercase, no spaces.

Usage

import { Alert, Callout } from "@luc/ui/alert";
<Alert
  tone="warning"
  title="Delayed data"
  description="Yahoo returned end-of-day prices for the last two sessions."
/>

`Callout` takes the same props with the persistent intent; reach for it when the message is context rather than a status.

API

Read from the published TypeScript API, so a prop that is not listed here does not exist and a default that is listed is the one the compiler sees.

`Alert` and `Callout` are one implementation with two intents, and `AlertTitle`, `AlertDescription` and `AlertActions` are the parts that `title`, `description` and `actions` are shorthand for. The two public components are open above; the parts are folded underneath.

Exported symbols
ExportKindSignature
Alert9 documented props({ className, tone, emphasis, title, description, icon, actions, onDismiss, dismissLabel, live, children, ...props }: AlertProps) => import("@types/react").JSX.Element
AlertActionsexport({ className, ...props }: AlertActionsProps) => import("@types/react").JSX.Element
AlertDescriptionexport({ className, ...props }: AlertDescriptionProps) => import("@types/react").JSX.Element
AlertTitleexport({ className, ...props }: AlertTitleProps) => import("@types/react").JSX.Element
Callout9 documented props({ emphasis, live, ...props }: AlertProps) => import("@types/react").JSX.Element

Alert props

Type: AlertProps

Values the compiler reads from the declaration.

Alert props
PropTypeDefaultNotes
actions?ReactNode—
description?ReactNode—
dismissLabel?string | undefined—
emphasis?"alert" | "quiet" | undefined—`alert` (default) is the stronger surface for a status message; `quiet` is the persistent Callout treatment.
icon?ReactNode—Override the tone icon; pass `null` for a text-only block.
live?AlertLive | undefined—
onDismiss?(() => void) | undefined—Renders a dismiss control. Persistence is the consumer's job.
title?ReactNode—
tone?AlertTone | undefined—

Callout props

Type: AlertProps

Values the compiler reads from the declaration.

Callout props
PropTypeDefaultNotes
actions?ReactNode—
description?ReactNode—
dismissLabel?string | undefined—
emphasis?"alert" | "quiet" | undefined—`alert` (default) is the stronger surface for a status message; `quiet` is the persistent Callout treatment.
icon?ReactNode—Override the tone icon; pass `null` for a text-only block.
live?AlertLive | undefined—
onDismiss?(() => void) | undefined—Renders a dismiss control. Persistence is the consumer's job.
title?ReactNode—
tone?AlertTone | undefined—

Guidelines

Do

  • Use an Alert for a status the reader may need to act on, and a Callout for context that stays true while the page is open.
  • Say what happened and what the reader can do; a tone without a next step is decoration.
  • Keep one page-level Alert per region — two competing messages is a hierarchy that has stopped working.

Don't

  • Do not force `live` on content that is present when the page loads: it is already part of the page, and announcing it interrupts the reader.
  • Do not report a field's own validation error in an Alert — that error belongs to the field.
  • Do not use an Alert for a transient confirmation; that is a toast, and it should not occupy layout.

Accessibility

`live` is the only thing that decides whether a screen reader is interrupted: `off` (the default) renders no role, `polite` renders `role="status"`, and `assertive` renders `role="alert"`. The dismiss control is a real button with a name the caller can localise (`dismissLabel`), and the tone is carried by an edge and an icon as well as by colour, so the state survives a monochrome rendering.

Luc UI v1.0Alertdark theme