# Alert

> A message about the state of something, with an icon and an announcement role that follow the colour.

An alert tells the reader something about the page or an action they took. The colour picks the icon and the ARIA role together: `danger` and `warning` announce themselves as `role="alert"`, everything quieter is a polite `role="status"`.

## Colors

```javascript
stack({ gap: 'sm' },
  alert({ color: 'primary', title: 'Heads up' }, 'A new version of sitelo is available.'),
  alert({ color: 'success', title: 'Deployed' }, '169 pages published in 1.7 seconds.'),
  alert({ color: 'warning', title: 'Slow page' }, 'One page took over 500 ms to render.'),
  alert({ color: 'danger', title: 'Build failed' }, 'Two internal links point at pages that do not exist.'),
  alert({ color: 'neutral', title: 'Note' }, 'Islands are disabled in this project.'),
)
```

## Without a title

A one-line alert does not need a heading above the sentence.

```javascript
stack({ gap: 'sm' },
  alert({ color: 'success' }, 'Saved.'),
  alert({ color: 'danger' }, 'That email address is already in use.'),
)
```

## Variants

```javascript
stack({ gap: 'sm' },
  alert({ color: 'warning', variant: 'soft', title: 'Soft' }, 'The default — a tinted surface.'),
  alert({ color: 'warning', variant: 'outline', title: 'Outline' }, 'Transparent, with a coloured border.'),
  alert({ color: 'warning', variant: 'solid', title: 'Solid' }, 'The full palette colour, for something that must not be missed.'),
)
```

## Icons

Each colour has a default icon. Pass your own markup as `icon`, or `icon: false` for none.

```javascript
stack({ gap: 'sm' },
  alert({ color: 'primary', icon: false, title: 'No icon' }, 'Just the text.'),
  alert({
    color: 'primary',
    title: 'A custom icon',
    icon: icon('star'),
  }, 'Any SVG works — icons are markup, not a dependency.'),
)
```

## Dismissible

The close button carries its own handler:

`Rendered markup`

```html
onclick="import('/su/alert.js').then(m=>m.dismiss(this))"
```

So the alert below really closes with nothing imported on this page. If that module never arrives, the button renders and does nothing, which is why an alert should never be the only place a message appears.

```javascript
alert({ color: 'primary', title: 'Dismissible', dismissible: true },
  'Click the × — the handler fetches itself on the first press.',
)
```

## Rich content

Alerts take any children, so an action or a list can live inside one.

```javascript
alert({ color: 'danger', title: 'Link check failed' },
  stack({ gap: 'sm' },
    text({ variant: 'small' }, 'Two links point at pages that were not generated:'),
    list({ plain: true },
      listItem({ title: '/docs/old-routing', description: 'linked from /docs' }),
      listItem({ title: '/blog/draft', description: 'linked from /blog' }),
    ),
    stack({ direction: 'row', gap: 'sm' },
      button({ size: 'sm', color: 'danger' }, 'Show details'),
      button({ size: 'sm', variant: 'ghost', color: 'danger' }, 'Ignore'),
    ),
  ),
)
```

## Props

| Prop | Type | Default | Description |
| --- | --- | --- | --- |
| `color` | `'primary' \| 'neutral' \| 'success' \| 'warning' \| 'danger'` | `'primary'` | Picks the palette, the default icon and the ARIA role. |
| `variant` | `'soft' \| 'outline' \| 'solid'` | `'soft'` | How much weight the alert carries. |
| `title` | `Child` | — | Bold first line. |
| `icon` | `Child \| false` | — | Custom icon markup, or false for none. |
| `dismissible` | `boolean` | `false` | Adds a close button that imports its own handler. |
| `dismissLabel` | `string` | `'Dismiss'` | Accessible name for that button. |
