Irozon Design System — design foundations, tokens and components by Irozon AB.
Irozon Design System · v1.0
Components / Toast

A brief, auto-dismissing confirmation. Never a blocker.

.iz-toast is for the "Invoice saved" / "Couldn't save proposal" moment — a short-lived message that confirms an action just happened, then gets out of the way on its own. It never traps focus and never requires a dismiss click; a consumer's own timer controls how long it stays. First extracted from Irozon Tools, which had reinvented this exact pattern three times — once per feature area — before it moved here.

Region
.iz-toast-region — one per page/app shell, fixed to the viewport corner, aria-live="polite" + aria-atomic="true" so assistive tech announces each toast as it's inserted.
Toast
.iz-toast inside the region — an icon slot and a message. data-visible="true" (set a frame after mount) drives the entrance; removing it drives the exit.
Ownership
Presentation and motion only. Show/hide timing, the toast list, and message text are entirely the consuming app's responsibility — this file has no JavaScript dependency.
<div class="iz-toast-region" aria-live="polite" aria-atomic="true">
  <div class="iz-toast" data-variant="success" data-visible="true" role="status">
    <span class="iz-toast__icon" aria-hidden="true">✓</span>
    <span class="iz-toast__message">Invoice saved</span>
  </div>
</div>
Variants

The same four roles Badge and Status already use.

data-variant reuses Badge/Status's exact semantic set — no new colors, and dark theme resolves for free since every color is a token.

Invoice saved
Estimate not linked to a proposal
Couldn't save — try again
Draft restored from your last session
Invoice saved
Couldn't save — try again
What this component doesn't do

No queue, no timer, no dismiss button — on purpose.

Auto-dismiss timing, whether multiple toasts stack or replace each other, and any manual dismiss control are all application concerns, not this component's. A consumer that wants a close button composes one from .iz-button[data-icon-only] inside .iz-toast, the same way a field composes in its own inline action. Keeping the state machine out of the design system is deliberate — three different apps will reasonably want different auto-dismiss durations and stacking behavior, and none of that changes what a toast looks like.

Accessibility

Announced, not just shown.

Live region
aria-live="polite" on .iz-toast-region — a screen reader announces new toast text without interrupting whatever the user is doing, and without needing focus to move.
Full message, always
Put the complete sentence in .iz-toast__message — the icon is aria-hidden="true" and never the only way the outcome is communicated.
Never blocks input
At rest (data-visible absent, mid-exit) the toast is pointer-events: none — it can only ever be interacted with while actually visible, and even then it never traps focus or keyboard navigation the way a dialog does.
Reduced motion
The slide/scale entrance drops to a plain opacity fade — the toast still appears and is still announced, just without the motion.
React

Toast and ToastRegion from @irozon/design-system-react.

Toast owns the entrance/auto-dismiss animation for one item; ToastRegion is the aria-live container. The toast list — what's currently showing — stays your app's own state; this package doesn't manage a queue.

import { ToastRegion } from "@irozon/design-system-react";
import { useState, useCallback } from "react";

const [toasts, setToasts] = useState([]);
const showToast = useCallback((message, variant = "success") => {
  setToasts((t) => [...t, { id: Date.now(), message, variant }]);
}, []);

<ToastRegion
  toasts={toasts}
  onDismiss={(id) => setToasts((t) => t.filter((x) => x.id !== id))}
/>