Feedbackstable.toast
Toast
A short, non-blocking message that confirms what just happened and disappears — optionally with "Rückgängig".
Toasts are created from page code with toast(message, options) from js/toast.js; there is one toast region per page (bottom centre on phones above the tab bar, bottom end on desktop), created on first use. render() here only produces the same static markup for documentation and server-rendered previews.
Use a toast when the result of an action is not visible on screen ("Bestätigung an 23 Personen gesendet") and for reversible actions with an undo ("Lea als abwesend markiert" + "Rückgängig", UX guide §5.1). If the result is visible — a ticked row, a moved item — no toast is needed.
Look (design guide §9.4): inverted surface (--color-inverse-bg / --color-inverse-text), --shadow-md, a leading status icon, one line of text, the optional action as an underlined semibold text button. Status colour never goes on text there (it isn't tested on the inverse surface); success and danger put a small status fill behind the icon instead (§3.3). neutral shows the info icon without a fill.
Timing: plain toasts stay 4 s, toasts with an action at least 8 s (WCAG 2.2.1), tone: "danger" toasts stay until dismissed and use role="alert". Hovering or focusing a toast pauses the timer. Every toast has a close button.
Field errors never go into a toast — they belong to the form. Lasting problems belong in an alert.
Examples
Confirmation
Data
render({
"message": "Bestätigung an 23 Personen gesendet",
"tone": "success"
})Markup
<div class="toast" data-status="success">
<span class="toast__icon"><svg class="icon" aria-hidden="true"><use href="../../packages/ui/icons/icons.svg#check-circle"/></svg></span>
<span class="toast__text">Bestätigung an 23 Personen gesendet</span>
<button class="toast__action" type="button" data-close aria-label="Schliessen"><svg class="icon" data-size="sm" aria-hidden="true"><use href="../../packages/ui/icons/icons.svg#x"/></svg></button>
</div>Neutral
Data
render({
"message": "Training wurde in den Entwurf verschoben"
})Markup
<div class="toast">
<span class="toast__icon"><svg class="icon" aria-hidden="true"><use href="../../packages/ui/icons/icons.svg#info"/></svg></span>
<span class="toast__text">Training wurde in den Entwurf verschoben</span>
<button class="toast__action" type="button" data-close aria-label="Schliessen"><svg class="icon" data-size="sm" aria-hidden="true"><use href="../../packages/ui/icons/icons.svg#x"/></svg></button>
</div>With undo
Data
render({
"message": "Lea Müller als abwesend markiert",
"actionLabel": "Rückgängig"
})Markup
<div class="toast">
<span class="toast__icon"><svg class="icon" aria-hidden="true"><use href="../../packages/ui/icons/icons.svg#info"/></svg></span>
<span class="toast__text">Lea Müller als abwesend markiert</span>
<button class="toast__action" type="button" data-act>Rückgängig</button>
<button class="toast__action" type="button" data-close aria-label="Schliessen"><svg class="icon" data-size="sm" aria-hidden="true"><use href="../../packages/ui/icons/icons.svg#x"/></svg></button>
</div>Failure
Data
render({
"message": "Speichern fehlgeschlagen. Wird erneut versucht, sobald du online bist.",
"tone": "danger"
})Markup
<div class="toast" data-status="danger" role="alert">
<span class="toast__icon"><svg class="icon" aria-hidden="true"><use href="../../packages/ui/icons/icons.svg#alert-circle"/></svg></span>
<span class="toast__text">Speichern fehlgeschlagen. Wird erneut versucht, sobald du online bist.</span>
<button class="toast__action" type="button" data-close aria-label="Schliessen"><svg class="icon" data-size="sm" aria-hidden="true"><use href="../../packages/ui/icons/icons.svg#x"/></svg></button>
</div>Data contract
What render(data) accepts — validated against this JSON Schema in development and tests. Unknown fields are rejected. The same schema is in components.json.
| Field | Type | Required | Default | Description |
|---|---|---|---|---|
message | string | required | — | What happened, in past tense ("Anwesenheit gespeichert"). min 1 chars |
tone | "neutral" | "success" | "danger" | optional | "neutral" | Status, written as data-status with its icon: neutral (info icon), success (check-circle on a success fill, for confirmations), danger (alert-circle on a danger fill; role="alert", stays until dismissed). |
actionLabel | string | optional | — | Text of the optional action button, usually "Rückgängig". min 1 chars |
closeLabel | string | optional | "Schliessen" | Accessible name of the close button. |
JSON Schema
{
"type": "object",
"additionalProperties": false,
"required": [
"message"
],
"properties": {
"message": {
"type": "string",
"minLength": 1,
"description": "What happened, in past tense (\"Anwesenheit gespeichert\")."
},
"tone": {
"type": "string",
"enum": [
"neutral",
"success",
"danger"
],
"default": "neutral",
"description": "Status, written as `data-status` with its icon: `neutral` (info icon), `success` (check-circle on a success fill, for confirmations), `danger` (alert-circle on a danger fill; `role=\"alert\"`, stays until dismissed)."
},
"actionLabel": {
"type": "string",
"minLength": 1,
"description": "Text of the optional action button, usually \"Rückgängig\"."
},
"closeLabel": {
"type": "string",
"default": "Schliessen",
"description": "Accessible name of the close button."
}
}
}Markup & states
Root: div (inside .toast-region[aria-live="polite"]).toast — usable without render() by writing the markup directly.
Attributes
| Attribute | Values | Default | Description |
|---|---|---|---|
data-status | success | danger | (none = neutral) | Status: a status fill behind the icon. |
role="alert" | — | — | Set for danger toasts. |
Parts
| Part | Description |
|---|---|
.toast-region | Fixed container created by toast(); aria-live="polite". Offset above bottom bars with --toast-offset. |
.toast__icon | Leading status icon (decorative): info / check-circle / alert-circle; round status fill for success and danger. |
.toast__text | The message. |
.toast__action[data-act] | Optional action ("Rückgängig"). |
.toast__action[data-close] | Close button (x icon with aria-label). |
States
| State | Description |
|---|---|
[data-leaving] | Exit animation before removal. |
CSS custom properties
| Property | Default | Description |
|---|---|---|
--toast-offset | 0px | Extra distance from the bottom (set on the region or a parent), e.g. the height of a sticky bottom bar. |
Behaviour & events
Module: js/toast.js
import { toast } from '@clubhub/ui/js/toast.js' and call it; no markup or mount() needed.
API
| Function | Signature | Returns | Description |
|---|---|---|---|
toast | toast(message: string, { tone?: "neutral" | "success" | "danger", timeout?: number, action?: { label: string, onAction: () => void } }?) | () => void — dismiss function | Shows a toast in the page's toast region. timeout in ms; default 4000, 8000 with an action, 0 (stay until dismissed) for danger. onAction runs when the action is pressed, then the toast closes. The timer pauses on hover/focus and resumes with half the time on pointer leave. |
Accessibility
- The region is
aria-live="polite"; danger toasts userole="alert". - Toasts with an action stay at least 8 s and pause on hover/focus, so keyboard and screen-reader users can reach "Rückgängig".
- Never put the only way to do something into a toast; undo must also be possible elsewhere or within the timeout.
Guidance
Related: Alert / banner · Save status · Confirm dialog