Skip to content

Alert

Terminal window
$ npx shadcn@latest add https://lacodda.github.io/dowel/r/alert.json

The component lands in components/ui/alert.tsx and is yours to edit.

See it liveEvery tone, with and without an icon, a heading and an action - in either theme, and in the accent of any product of the line.→

When this and not a Toast, a Banner or a Dialog

Section titled “When this and not a Toast, a Banner or a Dialog”

The three messages look similar and mean different things, so the choice is about what the message is, not how much room it needs.

Reach for Alert when the message is a condition that is still true — and will still be true after a reload — about the thing it sits beside: this field could not be saved, this profile has no axes yet, this export is out of date. It sits in the flow of the page, next to what it is about.

Reach for Toast when something already happened, needs no decision, and stops being interesting immediately. If dismissing it would lose information, it is not a toast — that is an alert.

Reach for Banner when the condition is about the whole application rather than one part of it, and is true on every screen. An alert belongs to what it sits beside; a banner belongs to the whole screen.

Reach for Dialog when the reader has to answer something.

<Alert tone="warn" icon={<AlertTriangle />} title={t('notSaved')}>
{t('connectionDropped')}
</Alert>
<Alert
tone="info"
title={t('noAxesYet')}
action={<Button size="sm">{t('addOne')}</Button>}
>
{t('axesExplained')}
</Alert>

Everything but the body is optional, and nothing is drawn for a slot that was not given — a one-line alert comes out one line tall.

The icon is the product’s own, on purpose. An icon that means “warning” here should be the one it means everywhere else in the product, and a component library that ships its own set makes that impossible.

Colour is emphasis, never the message. An alert that means “failed” says so in words as well, so a reader who does not separate red from green gets the whole of it.

There is no default, deliberately.

An alert that appears because of something the reader just did should be role="alert", so it is announced. One that is simply part of the page — already there when the screen loaded — should have no role at all, or a screen reader interrupts whatever it was saying to read the furniture.

Only the product knows which it has.

{/* Already on the screen. Read in its turn. */}
<Alert tone="info">{t('exportIsStale')}</Alert>
{/* Appeared just now, because saving failed. Announce it. */}
<Alert tone="bad" role="alert">{t('couldNotSave')}</Alert>

Props

Read from the types in ui/alert.tsx, the file a product copies - so the description of each prop is also what an editor shows over it there.

PropTypeDefault
toneneutral | good | warn | bad | info'neutral'
iconReactNodeDrawn before the text. The product's own, because an icon that means "warning" here should be the one it means everywhere else in the product.
titleReactNodeThe heading. Optional: a one-line alert does not need one.
actionReactNodeAnything that goes at the end - a link to the thing that fixes it, a dismiss button the product owns.

And every prop of React's HTMLAttributes<HTMLDivElement> except title, passed on.

Accessibility

The examples on the stand, through axe 4.14.0 in a browser, in both themes, against WCAG 2.2 A and AA and axe's best practices: 10 rules applied and passed, and none failed. The accessibility report has every item.

History

Each release that changed ui/alert.tsx - the file a product copies - newest first.

  1. v0.39.0
    • feat let the frame go on paper, and keep a panel or a chart on one sheet 354d583
  2. v0.32.0
    • fix let an icon keep the size it is given, across the set 217b558
  3. v0.11.0
    • feat Toast, Alert and Banner 8401211