Toast
hc-toast is a transient notification. You don’t write toast markup — you
fire an event (or return an HX-Trigger header) and installToast()
renders a .hc-toast into a .hc-toast-region, auto-dismissing it after a
delay.
Toasts are rendered by the installToast() behavior — without it, firing
an hc:toast event does nothing. Install it once at startup:
import { installToast } from '@hypermedia-components/core';installToast(); // idempotent; returns an uninstallerThe zero-config @hypermedia-components/core/behaviors entry installs it
automatically. No markup is required — the behavior creates a default
.hc-toast-region if you don’t pre-render one.
Appearance
Section titled “Appearance”The behavior renders this markup for you — you never write it by hand.
<!-- Rendered by installToast() into the .hc-toast-region --><div class="hc-toast" data-variant="success" role="status"> <div class="hc-toast__title">Saved</div> <div class="hc-toast__body">Your changes were saved.</div></div>Firing a toast
Section titled “Firing a toast”Dispatch an hc:toast CustomEvent on document.body:
document.body.dispatchEvent(new CustomEvent('hc:toast', { bubbles: true, detail: { message: 'Saved.', title: 'Done', variant: 'success', duration: 4500 },}));Try it — each button fires the event. The toast appears in the page corner and auto-dismisses:
// installToast() must be installed (it is in the behaviors bundle).successButton.addEventListener('click', () => { document.body.dispatchEvent(new CustomEvent('hc:toast', { bubbles: true, detail: { title: 'Done', message: 'Saved.', variant: 'success' }, }));});…or let the server fire it from a response — htmx turns an HX-Trigger
header into the same event:
HX-Trigger: {"hc:toast": {"message": "Saved.", "variant": "success"}}detail fields: message (required), title?, variant?
(info · success · warning · error), duration? ms (0 keeps it until
dismissed), id? (see below), action? (see below). error is announced
assertively (role="alert"); others use role="status".
Actions & updates
Section titled “Actions & updates”Action button. Add action: { label, event } and the toast renders a
button; clicking it dispatches a bubbling CustomEvent named event (then
dismisses the toast). Catch it with a plain listener or htmx
hx-trigger="<event>" on any ancestor — handy for Undo:
document.body.dispatchEvent(new CustomEvent('hc:toast', { bubbles: true, detail: { message: 'Item deleted', duration: 0, action: { label: 'Undo', event: 'hc:undo' } },}));document.body.addEventListener('hc:undo', () => restoreItem());Update by id. Give a toast an id; a later hc:toast with the same
id updates it in place (re-rendering the message / variant and resetting
the auto-dismiss timer) instead of stacking a new one. That models a
loading → success / error promise without any client state — the network
stays with htmx:
# request fires a sticky loading toastHX-Trigger: {"hc:toast": {"id": "save-42", "message": "Saving…", "duration": 0}}
# the response updates the same toastHX-Trigger: {"hc:toast": {"id": "save-42", "message": "Saved!", "variant": "success", "duration": 4500}}The toast only models the UI states; htmx performs the actual request.
The region
Section titled “The region”Toasts render into the first [data-hc-toast-region]. If none exists the
behavior creates one (bottom-right) for you. Pre-render it to configure
position and stacking:
<div class="hc-toast-region" data-hc-toast-region data-position="top-center" data-limit="3" role="region" aria-label="Notifications"></div>Position
Section titled “Position”data-position anchors the region — {top,bottom}-{left,center,right}
(default bottom-right). Top positions stack downward (newest nearest the
edge); bottom positions stack upward.
<div class="hc-toast-region" data-hc-toast-region data-position="top-right" …></div>Stacking limit
Section titled “Stacking limit”data-limit="N" caps the number of visible toasts; when a new one arrives
the oldest is evicted. Without it the stack is unbounded.
Swipe to dismiss
Section titled “Swipe to dismiss”Every toast can be dragged horizontally to dismiss it — past ~40% of its
width it flies out, otherwise it snaps back. This is a pointer / touch
affordance (keyboard users rely on auto-dismiss); vertical drags still scroll
the page. The motion is removed under prefers-reduced-motion.
Accessibility
Section titled “Accessibility”- The region is a labelled
role="region"; the label is translatable via the i18n catalog (toast.label). - Each toast is a live region —
role="alert"/aria-live="assertive"forerror,role="status"/aria-live="polite"otherwise — so it is announced without moving focus. - Don’t rely on color alone: include a
titleor clearmessage. - Keep
durationlong enough to read (or0for important messages), since swipe-to-dismiss isn’t available to keyboard users.
Theming tokens
Section titled “Theming tokens”| Token path | Purpose |
|---|---|
toast.region-inset / region-gap | Region offset from the edge / gap between toasts. |
toast.gap | Gap between title and body. |
toast.min-width / max-width | Toast width bounds. |
toast.padding-y / padding-x / radius | Box metrics. |
toast.{variant}.{bg,fg,border} | Per-variant colors. |
CSS variables
Section titled “CSS variables”Show the generated CSS variables
--hc-toast-region-inset | -region-gap--hc-toast-gap | -min-width | -max-width | -padding-y | -padding-x | -radius--hc-toast-info-bg | -fg | -border--hc-toast-success-bg | -fg | -border--hc-toast-warning-bg | -fg | -border--hc-toast-error-bg | -fg | -borderRelated
Section titled “Related”- Alert — inline, persistent notice.
- htmx integration — firing toasts from
HX-Trigger.