Toast
The .c-toast is a transient notification that slides in from the screen
edge. Unlike alerts (inline), toasts are overlay-level (z-index 1100) and
auto-dismiss via a JavaScript timer. The CSS handles the visual
entrance/exit via @starting-style.
Semantic HTML
<div
class="c-toast-stack"
data-placement="bottom-end"
role="region"
aria-label="Notifications"
>
<div class="c-toast" data-tone="success" data-visible role="status">
<span class="c-toast__icon"><svg>...</svg></span>
<div class="c-toast__content">
<span class="c-toast__title">Saved</span>
<span class="c-toast__body">Changes were saved.</span>
</div>
<button class="c-toast__close" aria-label="Dismiss">x</button>
</div>
</div>
Enter/exit
The [data-visible] attribute triggers the enter transition. Removing it
triggers the exit. JavaScript should set this attribute when adding a toast,
and remove it (then delete the element after the transition) on dismiss.
Timing requirement
@starting-style only fires when a property changes for the first time on
an element already in the DOM. If you insert the toast element and add
data-visible in the same task/microtask (e.g. inside an Alpine x-init, a
React useEffect, or a plain innerHTML assignment followed immediately by
setAttribute), the browser batches both operations and @starting-style
never sees the “before” state — the toast appears instantly with no animation.
Fix: defer setting data-visible to the next animation frame or microtask
after the element is in the DOM:
// Vanilla JS
stack.appendChild(toastEl);
requestAnimationFrame(() => toastEl.setAttribute("data-visible", ""));
// Alpine.js
// x-init="$nextTick(() => $el.setAttribute('data-visible', ''))"
This is not a Porchlight bug — it is how @starting-style works per spec.
The deferral ensures the browser registers the initial state (opacity: 0,
translated right) before the transition to the visible state.
Stack placement
.c-toast-stack is fixed by default at the block-end / inline-end corner. Use
data-placement when an app shell needs another corner:
<div
class="c-toast-stack"
data-placement="top-end"
role="region"
aria-label="Notifications"
>
...
</div>
Supported values:
bottom-end(default)bottom-starttop-endtop-start
Class contract
| Selector | Role |
|---|---|
.c-toast-stack | Fixed container (corner placement). |
.c-toast | Individual toast notification. |
.c-toast__icon | Leading icon (tone-colored). |
.c-toast__content | Title + body wrapper. |
.c-toast__title | Bold heading. |
.c-toast__body | Supporting text (muted). |
.c-toast__close | Dismiss button. |
[data-visible] | Triggers enter transition. |
[data-tone] | success, warning, danger (default = accent). |
[data-placement] | Stack corner: bottom-end, bottom-start, top-end, top-start. |