A message that stays in the page until its matter is settled (also called a banner or callout). Reach for it when the reader must notice something here: a failed import, an approval waiting, a form that did not save.
import { Alert } from "@umriss-ui/core";
warning and danger interrupt a screen reader at once; neutral, accent and success are read out politely when the reader is free. Choose the tone by what the reader has to do, not by the colour you want.
Dismissing is reported through onDismiss and nothing more: whether and when the message comes back is your state.
A title and a sentence or two, standing where the matter is – here above a sprint board.
import { Alert } from "@umriss-ui/core";
export default function AMessage() {
return (
<Alert title="Sprint 14 ends on Friday">
Items still in progress on Friday at 17:00 move to Sprint 15 with their remaining estimate.
</Alert>
);
}
Pick the tone by what the reader has to do; warning and danger also interrupt a screen reader at once.
import { Alert, Stack } from "@umriss-ui/core";
export default function Tones() {
return (
<Stack gap={3}>
<Alert tone="neutral" title="Maintenance window tonight">
Search is read-only from 23:00 to 23:30 while the index is rebuilt.
</Alert>
<Alert tone="accent" title="New: latency by region">
Each service's page now splits its 95th percentile by region.
</Alert>
<Alert tone="success" title="Billing is back within its objective">
The 95th percentile has stayed below 400 ms for the last hour.
</Alert>
<Alert tone="warning" title="Webhook deliveries are delayed">
The oldest message in the queue is seven minutes old. Deliveries still go out.
</Alert>
<Alert tone="danger" title="Checkout is failing card payments">
About 3 % of payments have timed out since 09:42. INC-1048 is open.
</Alert>
</Stack>
);
}
Put the next step in actions, under the text it belongs to, rather than somewhere else on the page.
import { Alert, Button } from "@umriss-ui/core";
export default function WithActions() {
return (
<Alert
tone="warning"
title="Invoice INV-26-0318 is waiting for your approval"
actions={
<>
<Button size="sm" variant="primary">
Review the invoice
</Button>
<Button size="sm" variant="ghost">
Pass it on
</Button>
</>
}
>
It is due on 15 April and books € 1,965.84 to Facilities (CC-4400).
</Alert>
);
}
onDismiss adds a close button and reports the click; whether the message ever comes back is your state.
import { useState } from "react";
import { Alert, Button } from "@umriss-ui/core";
export default function Dismissible() {
const [open, setOpen] = useState(true);
return open ? (
<Alert tone="accent" title="Tours now show their delivery windows" onDismiss={() => setOpen(false)}>
Each stop carries the window the customer booked; late arrivals are marked in the tour list.
</Alert>
) : (
<Button size="sm" onClick={() => setOpen(true)}>
Show the message again
</Button>
);
}
When a save fails, one danger alert above the form names every field that stops it and leads to the first.
import { useState } from "react";
import { Alert, Button, FormField, Input, Stack } from "@umriss-ui/core";
export default function AboveAForm() {
const [tried, setTried] = useState(false);
const [owner, setOwner] = useState("");
const [budget, setBudget] = useState("");
const missing = [owner === "" ? "Owner" : null, budget === "" ? "Monthly budget" : null].filter((name) => name !== null);
const showErrors = tried && missing.length > 0;
return (
<Stack gap={4} style={{ maxWidth: 440 }}>
{showErrors && (
<Alert
tone="danger"
title="The cost centre was not saved"
actions={
<Button size="sm" onClick={() => document.getElementById(owner === "" ? "cc-owner" : "cc-budget")?.focus()}>
Go to the first field
</Button>
}
>
{missing.join(" and ")} {missing.length === 1 ? "is" : "are"} still empty.
</Alert>
)}
<FormField label="Name">
<Input defaultValue="Customer service" />
</FormField>
<FormField label="Owner" required fieldId="cc-owner" error={showErrors && owner === "" ? "Name the person responsible." : undefined}>
<Input value={owner} onChange={(event) => setOwner(event.target.value)} />
</FormField>
<FormField label="Monthly budget (€)" required fieldId="cc-budget" error={showErrors && budget === "" ? "Enter an amount." : undefined}>
<Input numeric value={budget} onChange={(event) => setBudget(event.target.value)} />
</FormField>
<Button variant="primary" onClick={() => setTried(true)} style={{ alignSelf: "flex-start" }}>
Save the cost centre
</Button>
</Stack>
);
}
| Key | Action |
|---|---|
Tab |
Reaches the actions, then the dismiss cross. |
Enter / Space |
On the cross, calls onDismiss; the alert goes when you remove it. |
The keys of Button apply here.
The role follows the tone: warning and danger are an alert, which interrupts a screen reader; neutral, accent and success are a polite status, read when the reader is free.
The title is a paragraph, not a heading, so an alert does not enter the page's outline. The dismiss cross is named "Close message", or by dismissLabel.
AlertProps| Name | Type | Default | Description |
|---|---|---|---|
tone | AlertTone| "neutral" | "neutral" | How urgent the message is. Only warning and danger interrupt the
screen reader - the other two report politely.Shown in: Configurator, Tones, Offer the way forward and 5 more |
title | ReactNode | — | Heading; without it the body text carries the message on its own. Shown in: A message, Tones, Offer the way forward and 5 more |
actions | ReactNode | — | Actions below the text, e.g. "Try again". Shown in: Offer the way forward, Sum up a form's errors, A deployment (Stepper) |
dismissLabel | string | the wording's "Close message" | Label of the dismiss cross. |
ref | Ref<HTMLDivElement> | — | Receives the component's root element. from RefPropShown in: A row that holds still (Sizes), An anchored surface (Popover), Take the width of the trigger (Popover) and 2 more |
| Name | Type | Default | Description |
|---|---|---|---|
onDismiss | () => void | — | Shows a dismiss cross and calls this. Shown in: Let the reader dismiss it |
Also takes every attribute of <div> – without title.
AlertToneHow urgent an alert is; it also decides whether a screen reader is interrupted.
type AlertTone = "neutral" | "accent" | "success" | "warning" | "danger";What umriss deliberately does not build, and why: ADR-0032.