Notification
A standalone status card — a tinted glyph, a title and one supporting line, an
optional confirm action, and an optional close button. It owns its surface but
nothing about timing or stacking: use it on its own, or drop it inside a
Toast when you want it to appear, stack, and dismiss on its
own. For a choice the user must make, use Dialog.
Import
import { Notification } from "@photon-ai/pho-ui/components/notification";
Colors
color is a closed, semantic set — it selects the glyph and its tint together.
The brand card below also shows the reduced action row: a confirm link paired
with a dismiss link.
New workspace invite
Olivia added you to the Photon team.
Draft saved
Your changes are stored locally.
Deployment ready
photon-web is live in production.
Usage nearing limit
You've used 90% of this month's build minutes.
Deploy failed
The build step exited with code 1.
<Notification
color="success"
title="Deployment ready"
description="photon-web is live in production."
onClose={() => {}}
/>
color |
Use it for |
|---|---|
brand |
Informational / product update (default) |
gray |
Neutral, low-stakes note |
success |
An action completed |
warning |
Needs attention, not yet failed |
error |
Something went wrong |
Actions and dismissal
Two independent, optional pieces. Passing confirmLabel (with onConfirm)
renders the action row: a brand confirm link beside a dismiss link whose text is
dismissLabel (default "Dismiss"). Passing onClose renders the top-right
close button — and wires the dismiss link, when the row is present, to the same
handler. A card with neither is a read-only note.
Deploy failed
The build step exited with code 1.
Deployment ready
photon-web is live in production.
Draft saved
Your changes are stored locally.
// Full row + close button
<Notification
color="error"
title="Deploy failed"
description="The build step exited with code 1."
confirmLabel="Retry"
onConfirm={retry}
onClose={dismiss}
/>
// Close button only
<Notification title="Deployment ready" onClose={dismiss} />
// Read-only note — no actions
<Notification title="Draft saved" description="Stored locally." />
With Toast
The card is not a lifecycle. For a transient, auto-stacking notification, render
it as the child of a Toast.Root whose own surface you neutralize
(className="bg-transparent shadow-none ring-0") — the
Toast manager owns motion, stacking, and swipe-to-dismiss,
while the Notification owns the look. Wire the card’s onClose back to
manager.close(toast.id).
function ToastList() {
const manager = Toast.useToastManager();
return manager.toasts.map((toast) => (
<Toast.Root
key={toast.id}
toast={toast}
className="bg-transparent shadow-none ring-0"
>
<Notification
color={toast.data?.color ?? "brand"}
title={toast.title}
description={toast.description}
onClose={() => manager.close(toast.id)}
/>
</Toast.Root>
));
}
// Fire one:
manager.add({
title: "Deployment ready",
description: "photon-web is live in production.",
data: { color: "success" },
});
Anatomy
- Glyph + tint — chosen together by
color; override just the glyph withicon - title / description — a semibold line and one supporting line (
ReactNode) - Action row — appears only with
confirmLabel: a brand confirm link beside adismissLabellink (default"Dismiss"), wired toonConfirm/onClose - Close button — top-right; rendered only when you pass
onClose
Props
Notification is a single element; extra HTMLAttributes<HTMLDivElement> (such
as role, aria-live, id) spread onto its root <div>.
- title —
ReactNode— the semibold heading line (required) - description —
ReactNode— one supporting line under the title - color —
brand·gray·success·warning·error(defaultbrand) — selects the glyph and its tint together - icon —
Icon(Tabler) — override the status glyph while keeping the color’s tint - confirmLabel —
string— confirm link text; its presence renders the action row - onConfirm —
() => void— called when the confirm link is clicked - dismissLabel —
string(default"Dismiss") — dismiss link text in the action row - onClose —
() => void— called on dismiss; its presence renders the top-right close button - className —
string— merged onto the card surface - ref —
Ref<HTMLDivElement>— forwarded to the root<div>
Accessibility
- Semantics — the card renders a plain
<div>; it does not announce itself. Meaning comes from thetitle/descriptiontext, never color alone — the status glyph isaria-hidden. When a standalone card must be announced, passrole="status"(orrole="alert"forerror), which spreads onto the root. Fired through a Toast, the toast viewport already provides the live region. - Controls — the confirm and dismiss actions are real
<button>s with visible text. The icon-only close button carriesaria-label="Dismiss"and hides itsXglyph from assistive tech. - Focus — the confirm/dismiss links and the close button each show a
focus-visiblering; nothing is auto-focused when the card mounts.
Keyboard:
- Tab — move through the confirm link, the dismiss link, then the close button (in that DOM order)
- Enter / Space — activate the focused control
There is no built-in Esc handler — the card is not a dialog, so dismissal is
always an explicit control (or the toast’s swipe).
Best practices
- Say what happened, not what to decide — a title plus one line. Anything that
needs a real decision belongs in a
Dialog, not here. - Keep the action row to the one thing worth doing right now (
Undo,View,Retry). It is a link pair, not a button bar. - Match
colorto meaning:errorandwarningfor problems,successfor confirmations,brand/grayfor neutral information. - Don’t lean on color alone — the title has to carry the message for anyone who can’t see the tint.
- When firing as a toast, don’t auto-dismiss
errorcards too fast — let people read and dismiss them.