Toast
Small notifications that come in at a corner of the window, wait and leave: "Message sent", "Link copied", "Something went wrong".
Documentation
-
Copy to Webflow
Add Geist and Geist Mono to your site's fonts (Site settings, Fonts, Google Fonts) before pasting: Webflow removes any font your site has not installed. -
Add custom CSS
Custom CSS in WebflowThis CSS already ships inside the Webflow embed, so a pasted section needs nothing added. Paste it yourself, in the page or the site custom code, only if the effect runs on several pages. The tutorial explains where.
See the tutorialThe look of a toast is thetoast-plateclass of the paste and the classes inside it: restyle them in the Designer. The stack has no block: the Embed places it, and reads four variables you can set in a rule of your own on the body,--toast-lineand--toast-errorfor the two colours of the line,--toast-widthfor the width of a toast and--toast-edgefor its distance to the window. In the stack the Embed gives the plate the width of the stack, no margin, and a 28px cross for its close; everything else is yours. The rows are Link blocks, because Webflow has no button that holds layers. -
Add custom JS
Custom JavaScript in WebflowThis script already ships inside the Webflow embed, settings block included. You paste it yourself, before the closing body tag of the page or the site custom code, only when the effect has to run on several pages, and the tutorial says how to choose.
See the tutorial -
Use it on your own elements
How to add a custom attribute in WebflowA custom attribute is a pair, a name and a value, typed in the Settings panel of the element you selected, under Custom attributes. The name is what the script looks for, the value is what it reads. The tutorial shows the panel.
See the tutorialCustom attributesSelect the element on the canvas and open the Settings panel. Under Custom attributes, add a pair: the name on the left, the value on the right.Custom attribute What it does data-toast-template Marks the toast written in the page: the model every toast is a copy of, with your classes. It takes no value: leave the value field empty. The engine hides it on the published page; the canvas of the Designer shows it, which is where it is restyled. The settings below go on it. Without one the engine builds a plain ink plate, and the settings are then set in the settings block. data-toast="Link copied." On what fires a toast: a button, a link, any element of the page, as many as you like. Its value is the words of the toast, fired when the element is pressed. Pressed again while its toast is there, it starts that toast again. Without any such element toasts are only fired from a script or by a form. [ Required ] data-toast-kind="success" Beside data-toast: the kind of that toast, info (a dot), success (a tick that draws itself) or error (a stroke and a line in the error colour, read at once by a screen reader). Without it, the kind of the setting kind. [ Optional ] data-toast-duration="3000" Beside data-toast: how long that toast stays, in milliseconds, 0 to keep it until it is dismissed. Without it, the setting duration, or errorDuration for an error. [ Optional ] data-toast-action="Undo" Beside data-toast: adds an action with this word to the toast. Pressed, it closes the toast and fires toast:action. [ Optional ] data-toast-href="#basket" Beside data-toast-action: where the action leads. Without it the action leads nowhere and only closes the toast. [ Optional ] data-toast-id="saved" Beside data-toast: names the toast. Two elements with the same id share one toast, which the second writes over. Without it each element has its own, started again when the element is pressed again. [ Optional ] data-toast-text In the template: the element the words of a toast are written in. It takes no value. Without it they are written in the template itself, in place of everything it holds. [ Optional ] data-toast-mark In the template: the box the mark is drawn in, a dot, a tick or a stroke. It takes no value. Without it a toast has no mark. [ Optional ] data-toast-action In the template, with no value: the link of an action. Only a toast that has an action shows it. [ Optional ] data-toast-close In the template: the close. It takes no value. Its word stays its name for a screen reader and a cross is drawn in its place. Without it a toast is still dismissed by Escape, by a swipe and by its time. [ Optional ] data-toast-sent="Thank you, Pam." On a form of the library (Form 01, Form 02): its own words for the toast of a send that succeeded. data-toast-failed does the same for a send that failed, and data-toast-forms="off" keeps that form from firing any. [ Optional ] data-toast-state Written by the engine. On the stack: open. On a toast: in while it arrives, out while it leaves. A toast also carries data-toast-kind, and data-toast-back when it stands behind the newest. Never written by hand; the stylesheet reads them, and so can a rule of yours. [ Optional ] SettingsEvery setting is a custom attribute too, added the same way and on the same element. An attribute always wins over the defaults written in the code.Attribute Default What it is data-toast-position bottom-right The corner of the window the toasts come in at. Options: bottom-right, bottom-left, bottom-center, top-right, top-left, top-center. Under 480 px of width the stack is always at the foot of the window, edge to edge. data-toast-max 3 How many toasts are on screen at once. A toast fired beyond that waits in a queue: the oldest toast on screen gives it its place once it has been seen for a second and a half, and a toast that waits for an answer or was told to stay keeps its own. data-toast-duration 5000 How long a toast stays, in milliseconds, which is also the time its line takes to leave. data-toast-error-duration 8000 How long a toast of the kind error stays, in milliseconds. data-toast-kind info The kind of a toast fired without one. Options: info, a dot; success, a tick that draws itself; error, a stroke in the error colour, a line of that colour, and an announcement that does not wait. data-toast-forms on Whether the forms of the library fire a toast when they are sent and when their send fails. Options: on; off. data-toast-forms="off" on one form leaves that form alone. data-toast-sent Message sent. The words of the toast when a form of the library is sent. Options: any text, in the language of the page. data-toast-sent on one form gives it its own. data-toast-failed The message could not be sent. The words of the toast when the send of a form fails. Options: any text. data-toast-failed on one form gives it its own. data-toast-label Notifications The name of the stack for a screen reader, which lists it among the regions of the page. Options: any text, in the language of the page. data-toast-key F8 The key that moves the focus to the newest toast, and back to where it was when pressed again. Options: the name of a key as the browser gives it, F8, F6; off for none.
Two files, no build step.
It runs in the browser only. Load it with a script tag in plain HTML, in a
client-side script in Astro, never in the frontmatter. In React, call
Toast.mount(ref.current) in useEffect and
destroy() in its cleanup.
-
Tune it, either way
Per element with an attribute, or once for the whole site by editingDEFAULTSat the top of the file.Attribute Default What it is Lower Higher data-toast-position bottom-right The corner of the window the toasts come in at. Options: bottom-right, bottom-left, bottom-center, top-right, top-left, top-center. Under 480 px of width the stack is always at the foot of the window, edge to edge. data-toast-max 3 How many toasts are on screen at once. A toast fired beyond that waits in a queue: the oldest toast on screen gives it its place once it has been seen for a second and a half, and a toast that waits for an answer or was told to stay keeps its own. Fewer on screen; 1 shows one toast at a time and never stacks A taller stack when it opens; above 4 or 5 it covers the page on a phone data-toast-duration 5000 How long a toast stays, in milliseconds, which is also the time its line takes to leave. Leaves sooner; under 3000 a sentence is hard to read. 0 keeps the toast until it is dismissed More time to read, and a slower line data-toast-error-duration 8000 How long a toast of the kind error stays, in milliseconds. Leaves sooner More time to read what went wrong. 0 keeps an error until it is dismissed data-toast-kind info The kind of a toast fired without one. Options: info, a dot; success, a tick that draws itself; error, a stroke in the error colour, a line of that colour, and an announcement that does not wait. data-toast-forms on Whether the forms of the library fire a toast when they are sent and when their send fails. Options: on; off. data-toast-forms="off" on one form leaves that form alone. data-toast-sent Message sent. The words of the toast when a form of the library is sent. Options: any text, in the language of the page. data-toast-sent on one form gives it its own. data-toast-failed The message could not be sent. The words of the toast when the send of a form fails. Options: any text. data-toast-failed on one form gives it its own. data-toast-label Notifications The name of the stack for a screen reader, which lists it among the regions of the page. Options: any text, in the language of the page. data-toast-key F8 The key that moves the focus to the newest toast, and back to where it was when pressed again. Options: the name of a key as the browser gives it, F8, F6; off for none. -
Drive it from JavaScript
When a script knows what happened: the answer of a request, a toast that changes its words, a toast to take away.const element = document.querySelector('[data-toast-template]'); element.addEventListener('toast:show', (e) => { console.log(e.detail); }); // One toast that changes: Sending becomes Sent, or says what went wrong. const toast = Toast.show({ text: 'Sending the report', kind: 'loading' }); fetch('/api/report', { method: 'POST' }) .then((answer) => { if (!answer.ok) throw new Error(answer.status); toast.update({ text: 'Report sent.', kind: 'success' }); }) .catch(() => { toast.update({ text: 'The report could not be sent.', kind: 'error' }); }); Toast.show('Link copied.'); // a text is enough Toast.show({ text: 'Thread archived.', action: { label: 'Undo', onClick: () => console.log('undone') } }); document.addEventListener('toast:dismiss', (e) => { console.log(e.detail.id, e.detail.reason); // timeout, close, swipe, escape, action, script, room }); // When the toasts leave the page for good: Toast.destroy(); // removes the stack and every listener, shows the template again
-
Fit it to your page
Without code
Put
data-toast="Link copied."on a button, a link, any element. Pressing it fires the toast. Pressed again while its toast is still there, it starts that toast again, line included, instead of adding a second one.html <button data-toast="Link copied.">Copy the link</button> <button data-toast="Could not save. Try again." data-toast-kind="error">Save</button> <button data-toast="Thread archived." data-toast-action="Undo">Archive</button>A link whose
hrefis#stays put. A link that leads somewhere leaves the page, and its toast with it: a toast is for something that happened on this page.The toast itself
The toast is a block of your page, marked
data-toast-template: a mark, the words, an action, a close. Give it your classes, your colours, your corners, your type. The engine hides it, copies it for every toast and fills the copy; the line takes the corner radius it finds on it. In Webflow it is a block on the canvas like any other, which is where it is restyled.Without a template the engine builds a plain ink plate of its own, so
Toast.show()works on a page with no markup at all.The stack is built by the engine and has no block. Its width, its distance to the window and its colours are custom properties, set on the body or in a rule on
[data-toast-stack]. The values below are an example of such a rule, not the defaults, which are in the first part oftoast.css:css body { --toast-line: #ffab1a; /* the line around a toast */ --toast-error: #ff8f70; /* the line and the mark of an error */ --toast-width: 400px; /* a toast, on anything wider than a phone */ --toast-edge: clamp(12px, 2.2vw, 32px); /* from the stack to the window */ --toast-veil: #f4ece0; /* the colour of the page, which lightens the toasts behind */ } [data-toast-stack] { row-gap: 10px; /* between two toasts of the open stack */ column-gap: 10px; /* the strip an older toast shows behind the newest */ }From JavaScript
js // send() stands for a request of your own, which returns a promise. async function sendReport() { const toast = Toast.show({ text: 'Sending the report', kind: 'loading' }); try { await send(); toast.update({ text: 'Report sent.', kind: 'success' }); } catch (error) { toast.update({ text: 'The report could not be sent.', kind: 'error' }); } }Toast.show(options)fires a toast and returns it:{ id, element, update(options), dismiss() }.optionsis a text, or{ text, kind, duration, action, id }.kindisinfo,success,errororloading; aloadingtoast has no time and stays until it is updated or dismissed.durationis in milliseconds,0to keep the toast.actionis{ label, href, onClick }: a link in the toast, which closes it when pressed.idnames the toast: a secondshowwith the id of a toast still there updates that one.update(options)changes the words, the kind, the action or the time of a toast; its time starts again and its line draws itself again.Toast.dismiss(id)sends one toast away,Toast.dismiss()all of them and those that wait.Toast.mount(template, overrides)starts the engine with that element as the template, or with none, and returns{ element, settings, show(options), dismiss(id), destroy() }.overridestakes any setting key. There is one stack: mounted again, it reads again, and the new template replaces the first.Toast.auto(root)does it with the template it finds inroot, the document by default, and runs once on its own.Toast.destroy()removes the stack, every toast and every listener and shows the template again.Toast.defaultsis theDEFAULTSobject.- Events, fired on the template and bubbling to the document (on the document when there is no template):
toast:show,toast:update,toast:actionandtoast:dismiss, each withdetail{ id, kind, text, element, reason }.reasonsays why a toast left:timeout,close,swipe,escape,action,script, orroomwhen a full stack made place for a newer toast.
With the forms of the library
Form 01 and Form 02 fire
formset:sentandformset:failed. With the Toast on the same page, nothing is to be written: a form that left gets a toast with the words of the settingsent, a form whose send failed gets an error toast with the words offailed. One toast per form: a second try writes over the first.data-toast-sent="Thank you, Pam."anddata-toast-failedon a form give it its own words, anddata-toast-forms="off"on a form, or as a setting, leaves the forms alone.What the visitor can do
- The time stops while the pointer is on the stack, while the focus is in it, while a finger is on a toast and while the tab is hidden. The line stops with it, and both go on from where they were.
- The stack opens under the pointer and with the focus; on a touch screen a tap on it opens it and a tap elsewhere closes it.
- A toast is dismissed by its cross, by Escape (the toast that has the focus, or the newest), and on a touch screen by taking it sideways or towards the edge it came from.
- The keyboard reaches the toasts with the key of the setting
key, F8, which moves the focus to the newest one and back when pressed again; Tab then walks the crosses and the actions. When a toast that had the focus leaves, the focus goes to the next toast, or back to where it was. A toast never takes the focus by itself. - A screen reader hears each toast once: politely, when it has finished speaking, for information, and at once for an error. An updated toast is read again only if its words changed.
- On a phone, under 480 px, the stack is at the foot of the window from edge to edge whatever
positionsays, above the home bar, and above the keyboard when it is out. - **Under
prefers-reduced-motion: reduce** nothing travels: a toast fades in where it stands and fades out. The line still leaves, since it is the time left, but it does not draw itself first. - Without JavaScript nothing is fired and the template is not shown: the
noscriptline of the snippet hides it. - The words of the demo are the demo's: on a real page every toast says what really happened, in the page's language.
-
Avoid the pitfalls
- One stack per page. The Toast is not mounted on an element: it is one engine and one stack for the page. Pasted twice, the second copy finds the first and does nothing more; two templates, and the first is the model.
- A toast is not for what must be read. It leaves by itself. An error the visitor has to act on belongs beside the field, as the forms of the library do; give it
data-toast-duration="0"at least. - The stack must not sit inside anything that moves. The engine puts it at the end of the body, so it is fixed to the window. The template may be anywhere.
- In Webflow the template is on the canvas, the stack is not. The canvas runs no Embed: the block marked
data-toast-templateshows in the flow, where it is restyled, and its close reads as its word. On the published site the engine hides it, and the cross is drawn. Put it at the end of the page, or in a section of its own, so it is easy to find. - In Webflow a button is a Link block, so the plates of the paste are links to
#markeddata-toast: the engine keeps them from jumping to the top. - The mechanics lay the plate out in the stack: its width is the stack's, its margin is none, and the close is a 28 px cross. Change the rest on the template: padding, ground, colour, corners, shadow, type.
- **
Escapebelongs to the page too.** The engine leaves an Escape that something else already took (preventDefault), a dialog for one, and otherwise dismisses the newest toast, wherever the focus is. - Words only. A toast takes a text, written as text: markup in it is shown as it is typed.
- The stylesheet places the stack. Without
toast.cssthe toasts pile up at the end of the page, and the engine says so in the console. - **
Toastis the global.** A page that already has awindow.Toastof another library keeps it: this engine does not load over it, and says so in the console.
One click on Copy the AI prompt copies everything an assistant needs to build this effect: the complete code, the markup, the dependency, every setting and every pitfall.
What it does
Toast is a site utility component for Webflow and vanilla JavaScript, with no library. Also called toast notification, stacked toast notifications or toast with progress timer.
Small notifications that come in at a corner of the window, wait and leave: "Message sent", "Link copied", "Something went wrong". One line runs around each toast and is its clock: it draws itself around the plate when the toast arrives, then leaves it from its start, and the toast goes when the last of the line is gone. A toast that waits for an answer has a piece of that line going round, which becomes the whole line when "Sending" becomes "Sent". The time stops under the pointer, with the focus, under a finger and while the tab is hidden, and the line stops with it. Several toasts stack: an older one steps back behind the newest and shows a strip of itself, and the stack opens on hover, on focus or at a tap. A maximum number is on screen: toasts fired together come in one after the other, and the oldest gives its place to the one that waits. A toast is fired without code, by an attribute on any element, from JavaScript with Toast.show(), which returns the toast to update or dismiss, or by the forms of the library when they are sent or fail. It is dismissed by its cross, by a swipe on a touch screen, by Escape. Two live regions tell a screen reader, politely for information and at once for an error, and the focus is never taken. On a phone the stack is the full width of the foot of the window, above the keyboard. The toast itself is a block of the page, the template, so it is restyled like any other. No library. Pattern type toast notification, snackbar, alert.
Updated
Questions
Paste the component, then put the custom attribute data-toast on any button or link, with the words of the toast as its value. Pressing it fires the toast in the corner; data-toast-kind makes it a success or an error and data-toast-duration sets how long it stays, in milliseconds.
Yes. A line runs around each toast and leaves it over the duration, 5000 ms unless said otherwise, and the toast goes when the line is gone. The line and the time stop while the pointer is on the stack, while the focus is in it, under a finger and while the tab is hidden.
Toast.show({ text: 'Sending', kind: 'loading' }) returns the toast; call its update({ text: 'Sent.', kind: 'success' }) when the request answers, or with kind: 'error' when it fails. It stays the same toast: the piece of line that went round becomes the whole line and the time starts.
Each toast is read once, from two live regions built before any toast: politely for information, at once for the kind error. The focus is never moved to a toast; the key of the setting key, F8, goes to the newest one and back, and Escape dismisses it.
The toast is a block of the page marked data-toast-template, which the script copies for every toast: restyle it like any block, in the Designer or in CSS. Under 480 px the stack is the full width of the foot of the window, above the keyboard, whatever the setting position says.