Components
Toast & banner
Your messenger for what just happened, in two flavors. A toast is quick — it pops in, says its piece, and gets out of the way. A banner is steady — it stays right in the page until the situation clears. Both speak the same five-tone severity scale as Badge.
Toast
Fire it from anywhere — a mutation handler, a catch block — through the toast API; a store lines them up and the one mounted <ToastRegion> shows the stack. Each toast is its own live region (role=status, or alert for critical), so a screen reader announces it without stealing focus. Hover or focus the stack and the timer waits for you; criticals stick around until you dismiss them.
Fire a toast — it lands bottom-right, pauses on hover, and auto-dismisses (criticals stay).
Banner
The steady half — for something the page needs to keep saying, not a passing event. It sits right in the flow, solid and opaque, with the same tones, an optional action, and an optional dismiss.
Tones
The shared scale: neutral (a plain note), info, affirm (done — it worked), caution (worth a look), critical (failed or blocked). Each tone is a solid, friendly color — the metal names (silver, patina, copper, rust) belong to the proposed system. Per ADR-0017 every tone carries an icon, so severity survives grayscale and color-blindness; the color rides the accent and icon while the text stays plain and readable.
Materiality
Same tones, two surfaces — and both are solid here. The floating glass toast is a proposed-system idea; in the official brand a toast is a flat, opaque card with a soft warm shadow, and a banner sits flat in the page.
- toast surface
- Flat and opaque — the frosted glass (
card--sheet) belongs to the proposed system. Here the toast is a solid surface with a soft warm shadow at the highest lift, plus a tone accent bar and a tone icon. - banner surface
- Opaque — a
8%tone tint toward the surface, a tone accent bar, and a tone icon at -700 (light) / -300 (dark) for contrast on the tint. - region
- The stack portals to a body-level
#halo-toast-rootwhose id selector beats the page-pattern z-index pin, so the fixed region holds its corner. - motion
- Toasts pop in and slide out over
--dur-base; reduced-motion turns the animation off — and with no glass blur here, that's all there is to drop.
Accessibility
- Announced, not focused. Each toast is a live region (status / alert); focus never jumps to it, so a keyboard user is not interrupted mid-task.
- Pauses for the reader. Hover or focus the stack and the auto-dismiss timer stops, so a toast can't vanish while being read or acted on.
- Dismissable by keyboard. Every toast and dismissable banner has a real × button in the tab order.
- Severity is never colour alone. The icon (and the word) carry the meaning; the tone reinforces it (ADR-0017).
API
- toast(message, opts) / .affirm / .info / .caution / .critical
- Fire a toast and get its id. Tone helpers are shorthands; toast.dismiss(id) and toast.clear() remove them.
- ToastOptions
- title, duration (ms or
nullto stay), action ({ label, onPress }), icon, dismissLabel. - <ToastRegion>
- Mount once at the app root. placement, label, dismissLabel, max — the stack cap.
- <Banner>
- tone, title, children, icon, action, onDismiss, dismissLabel. Inline and controlled by its parent.
Usage
import { ToastRegion } from '@halo-compliance/ui'
// once, at the app root (e.g. in your shell):
<ToastRegion placement="bottom-end" />import { toast } from '@halo-compliance/ui'
async function onSave() {
try {
await saveRiskReport()
toast.affirm('Risk report saved.')
} catch {
toast.critical('Could not save the report.', {
action: { label: 'Retry', onPress: onSave },
})
}
}import { Banner, Button } from '@halo-compliance/ui'
<Banner
tone="caution"
title="Review needed"
action={<Button variant="ghost" tone="secondary" size="sm">Review</Button>}
onDismiss={() => setDismissed(true)}
>
Three findings need a responsible party before this report can close.
</Banner>When to use which
- Toast for a passing event. Saved, copied, export started — feedback that doesn't need to persist or block.
- Banner for a standing state. Draft, archived, review-needed — a fact about the page that stays until it changes.
- Modal for a decision. If the user must stop and choose, that blocks the page — reach for a Modal, not a message.
Anti-patterns
- A toast for an error that needs action. If the user must fix something, keep it on the page (a banner or inline error), not in a vanishing toast.
- Toasts as a log. They are momentary; the stack caps and the rest are dropped. A history belongs in the page.
- A banner with no way to act or dismiss. If it's actionable, give it an action; if it's transient, it was a toast.
- Tone for decoration. Pick the tone for the severity, not the look.
Related
- /badge — the same five-tone severity register, as a status label.
- /modal — the decision overlay, for when a message isn't enough.
- /helper-text — the inline field-level severity text; banner is its section-level sibling.
- /button — the solid-color actions inside a toast or banner.