Canvas Design System
Main Site Tokens

Toast

Confirms that something happened, without interrupting what the user is doing.

When to Use

Use when: An action succeeded or failed and the user doesn't need to do anything about it — saved, copied, deleted. Toasts confirm; they don't ask.
Don't use when: The message needs a decision or must not be missed — use Alert inline or a Modal. Toasts vanish on a timer, so anything the user must read doesn't belong in one. Don't stack more than three.

Variants

Four severities, each with its own icon. Every one is created from JavaScript — there is no PHP call that renders a toast.

wsToastSuccess('Changes saved.');
wsToastError('Something went wrong');
wsToastWarning('Please review');
wsToastInfo('New updates');

States

With an action

One action, typically Undo. Keep the window generous — the default four seconds is short for anyone navigating by keyboard.

wsToast('Item deleted', 'info', {
    duration: 10000,           // give people time to reach it
    action: { label: 'Undo', onClick: () => restoreItem(id) },
});

Duration

Four seconds by default. Longer for anything actionable, longer still for errors the user may want to read twice.

wsToastInfo('Quick toast', { duration: 2000 });
wsToastInfo('Long toast',  { duration: 8000 });
StateBehavior
AppearingThe toast is created and appended to #ws-toast-container, which is fixed-position and always in the DOM.
VisiblePersists for duration milliseconds — 4000 by default.
DismissingRemoved automatically on timeout, or immediately via the close button. There's no hover-to-pause, so a toast can expire while it's being read.
ClosableOn by default. Pass closable: false to omit the button — only sensible for very short, purely informational toasts.
StackingMultiple toasts stack in the container. Nothing caps the number, so guard against loops that could fire dozens.

Real-World Usage

The save-and-confirm pattern used across the Planner: success on the happy path, error with a longer duration on failure, and no toast at all when the user can already see the result.

async function savePlan(plan) {
    try {
        const res  = await fetch('/api/plans.php', { method: 'POST', body: JSON.stringify(plan) });
        const data = await res.json();
        if (!data.success) throw new Error(data.message);
        wsToastSuccess('Workshop saved');
    } catch (err) {
        // Errors get longer on screen than confirmations.
        wsToastError('Could not save — ' + err.message, { duration: 8000 });
    }
}

Options

PHP setup

Render the container once per page, usually in the footer. Without it, every wsToast() call silently does nothing.

<?= ws_toast_container() ?>

JavaScript API

FunctionPurpose
wsToast(message, variant, options)Full control
wsToastSuccess(message, options)Success shorthand
wsToastError(message, options)Error shorthand
wsToastWarning(message, options)Warning shorthand
wsToastInfo(message, options)Info shorthand

Options object

OptionTypeDefaultPurpose
messagestringFirst argument. One short sentence, no full stop needed.
variantstring'info'success | error | warning | info
durationnumber4000Milliseconds before auto-dismiss. Falsy values fall back to the default, so 0 will not make a toast persist.
closablebooltrueShow the close button. Only an explicit false removes it.
action.labelstringnullAction button text
action.onClickfunctionnullAction click handler

Accessibility

ConcernBehavior
Live regionThe container is aria-live="polite", so new toasts are announced once the screen reader finishes its current sentence — the right choice for confirmations, which shouldn't interrupt. Each toast carries its own aria-atomic="true", so only the new one is read, not the whole stack.
KeyboardThe close and action buttons are real buttons, reachable with Tab. Because the container is at the end of the document, reaching them may take many presses from where the user was.
Colour independenceVariant colour and icon are the only difference between success and error. Put the outcome in the words — "Workshop saved", not "Done".
ContrastAll variants render on the dark --toast-bg with --toast-text, a single tested pairing.
warning Two limitations worth knowing:
(1) Auto-dismiss conflicts with WCAG 2.2.1 (Timing Adjustable). Content disappears on a timer with no way to pause or extend it, and there's no hover-to-pause. Keep anything that matters out of a toast, and give actionable toasts a long duration.
(2) Error toasts are announced politely. A failure may be missed if the screen reader is mid-sentence. For errors that block the user's goal, put an Alert on the page as well.

Tokens

TokenUsed for
--toast-bg / --toast-textToast surface and message colour
--toast-radius / --toast-shadowCorners and elevation above the page
--text-inverseText on the dark surface
--mainsite-lightAction button accent
--gray-400Close button, resting
--radius-mdAction and close button corners
--space-3Internal spacing and the gap between stacked toasts
--text-caption / --text-metaMessage and action type scale
--font-body / --font-medium / --font-semiboldFace and weights
--transition-fastButton hover

CSS Classes

ClassPurpose
.ws-toast-containerFixed container, id="ws-toast-container" — the node the JS appends into
.ws-toastIndividual toast
.ws-toast--success / --error / --warning / --infoVariant colours
.ws-toast__iconVariant icon
.ws-toast__messageMessage text
.ws-toast__actionOptional action button
.ws-toast__closeDismiss button

Files

FilePurpose
includes/components/helpers.phpws_toast_container() helper function
includes/components/toast.phpThe container element and its live-region attributes
includes/components/components.cssStyles (.ws-toast rules)
includes/components/components.jswsToast() and the four shorthands