# Alert

Shows a message inside the page that people should read, such as a result, a warning or a notice.

- **Import:** `import { Alert, AlertTitle, AlertDescription } from "@booleanpress/ui/alert"`
- **APG Alert:** <https://www.w3.org/WAI/ARIA/apg/patterns/alert/>
- **Page:** <https://ui.booleanpress.com/components/alert> · @booleanpress/ui 0.1.0

## Usage

An alert is a box in the flow of the page. Put an icon first, then `AlertTitle` and `AlertDescription`. Each part is optional; with no icon the text starts at the edge.

```tsx
import { InfoIcon } from "lucide-react"
import { Alert, AlertDescription, AlertTitle } from "@booleanpress/ui/alert"

export function LoggingNotice() {
  return (
    <Alert variant="info">
      <InfoIcon />
      <AlertTitle>Logging is on</AlertTitle>
      <AlertDescription>Every email is kept for 30 days, then deleted.</AlertDescription>
    </Alert>
  )
}
```

`variant` sets the tone: `default` for a neutral note, `success`, `info` and `warning` on the status tokens, `destructive` for an error. Do not rely on colour alone: the title or the icon says what kind of message it is.

An alert has no close button and no action slot. For either, put a `Button` in the description (an action) or beside the alert (a dismissal), as the examples show. For a message that appears for a few seconds after an action, use a toast instead.

## Examples

### Basic

An icon, a title and a description.

```tsx
import { InfoIcon } from "lucide-react"
import { Alert, AlertDescription, AlertTitle } from "@booleanpress/ui/alert"

export default function AlertBasic() {
  return (
    <Alert className="w-full max-w-md">
      <InfoIcon />
      <AlertTitle>Logging is on</AlertTitle>
      <AlertDescription>Every email is kept for 30 days, then deleted.</AlertDescription>
    </Alert>
  )
}
```

### Tones

`success`, `info`, `warning` and `destructive`, each with the icon that names its tone.

```tsx
import { CircleAlertIcon, CircleCheckIcon, InfoIcon, TriangleAlertIcon } from "lucide-react"
import { Alert, AlertDescription, AlertTitle } from "@booleanpress/ui/alert"

export default function AlertTones() {
  return (
    <div className="flex w-full max-w-md flex-col gap-3">
      <Alert variant="success">
        <CircleCheckIcon />
        <AlertTitle>Connection verified</AlertTitle>
        <AlertDescription>The test email was accepted by the mailer.</AlertDescription>
      </Alert>
      <Alert variant="info">
        <InfoIcon />
        <AlertTitle>Backup mailer in use</AlertTitle>
        <AlertDescription>New emails go through the backup mailer until you switch back.</AlertDescription>
      </Alert>
      <Alert variant="warning">
        <TriangleAlertIcon />
        <AlertTitle>API key expires soon</AlertTitle>
        <AlertDescription>Create a new key before 12 October 2026.</AlertDescription>
      </Alert>
      <Alert variant="destructive">
        <CircleAlertIcon />
        <AlertTitle>Delivery failed</AlertTitle>
        <AlertDescription>The mailer rejected the sender address.</AlertDescription>
      </Alert>
    </div>
  )
}
```

### Without an icon

With no icon the text uses the full width.

```tsx
import { Alert, AlertDescription, AlertTitle } from "@booleanpress/ui/alert"

export default function AlertWithoutIcon() {
  return (
    <div className="flex w-full max-w-md flex-col gap-3">
      <Alert>
        <AlertTitle>Routing rules run in order</AlertTitle>
        <AlertDescription>The first rule that matches an email decides its mailer.</AlertDescription>
      </Alert>
      <Alert variant="warning">
        <AlertDescription>This mailer has no sender address yet.</AlertDescription>
      </Alert>
    </div>
  )
}
```

### With an action

A small button inside the description answers the message.

```tsx
import { TriangleAlertIcon } from "lucide-react"
import { Alert, AlertDescription, AlertTitle } from "@booleanpress/ui/alert"
import { Button } from "@booleanpress/ui/button"

export default function AlertWithAction() {
  return (
    <Alert variant="warning" className="w-full max-w-md">
      <TriangleAlertIcon />
      <AlertTitle>Another SMTP plugin is active</AlertTitle>
      <AlertDescription>
        <p>Both plugins try to send the same emails.</p>
        <Button size="sm" variant="outline" className="mt-2">
          Review plugins
        </Button>
      </AlertDescription>
    </Alert>
  )
}
```

### Dismissible

The page adds a ghost icon button, named with `aria-label`, and keeps the open state.

```tsx
import { useState } from "react"
import { InfoIcon, XIcon } from "lucide-react"
import { Alert, AlertDescription, AlertTitle } from "@booleanpress/ui/alert"
import { Button } from "@booleanpress/ui/button"

export default function AlertDismissible() {
  const [open, setOpen] = useState(true)

  if (!open) {
    return (
      <Button variant="outline" onClick={() => setOpen(true)}>
        Show the notice again
      </Button>
    )
  }

  return (
    <div className="relative w-full max-w-md">
      <Alert variant="info" className="pe-12">
        <InfoIcon />
        <AlertTitle>New in this version</AlertTitle>
        <AlertDescription>Routing rules can now match on the recipient domain.</AlertDescription>
      </Alert>
      <Button
        variant="ghost"
        size="icon-sm"
        aria-label="Dismiss the notice"
        className="absolute end-2 top-2"
        onClick={() => setOpen(false)}
      >
        <XIcon />
      </Button>
    </div>
  )
}
```

## Accessibility

**Semantics.** A `div` with `role="alert"`, which is an assertive live region: a screen reader reads it as soon as it appears in the page.

**Labels.** The alert's own text is read. Write the title so it makes sense alone, and keep the icon decorative (lucide icons are hidden from assistive technology by default).

**Focus.** An alert takes no focus and is not in the tab order. A button or link inside it is.

**Known limits.**

- Every alert is `role="alert"`, so several alerts rendered together on page load are all announced at once. Show only what needs attention, and use a plain `div` with the same classes for a quiet note.
- An alert that is present when the page loads is not announced by every screen reader; one added later is. Do not rely on it for the only copy of important text.
- `AlertTitle` keeps one line (`line-clamp-1`) and cuts longer titles with an ellipsis, so keep titles short.

### Keyboard

| Key | Behaviour |
| --- | --- |

## API

### Alert

Renders a `div` and passes it every other prop.

### AlertTitle

Renders a `div` and passes it every other prop.

### AlertDescription

Renders a `div` and passes it every other prop.

Every part takes `className`, merged with its defaults by `cn()`, and `ref`, which reaches the element it renders.

**Data attributes:** `data-slot="alert"` (Alert), `data-slot="alert-title"` (AlertTitle), `data-slot="alert-description"` (AlertDescription).

## Theming

The status variants read `--success`, `--warning` and `--info`: a 30–40 % border and a 5–10 % fill of the token on the card colour, with the icon in the token colour and the text in `--foreground`. `destructive` colours the text and icon with `--destructive`.

| Token | Used for |
| --- | --- |
| `--card` | background |
| `--card-foreground` | text |
| `--destructive` | text |
| `--foreground` | text |
| `--info` | border, background, text |
| `--muted-foreground` | text |
| `--success` | border, background, text |
| `--warning` | border, background, text |
