CSS + Datastar toasts
HTML
<script
type="module"
defer
src="https://cdn.jsdelivr.net/gh/starfederation/[email protected]/bundles/datastar.js"
></script>
<p>
Signal free CSS + Datastar toasts with in and out animations, progress bar,
and pause-on-hover. Add icons, close buttons, and style as needed.
</p>
<div class="toast-container"></div>
<button
data-on:click="
const container = document.querySelector('.toast-container');
const t = document.querySelector('#toast-template');
const n = t.content.firstElementChild.cloneNode(true);
container.appendChild(n);
"
>
Show toast
</button>
<template id="toast-template">
<div
class="toast toast-entering"
style="--toast-duration: 5000ms"
data-on:animationend="event.animationName?.startsWith('toast-in') && el.classList.remove('toast-entering');event.animationName?.startsWith('toast-out') && el.remove()"
>
<div class="toast-body">Toast: <span data-text="Math.random()"></span></div>
<div
class="toast-progress"
data-on:animationend="el.closest('.toast')?.classList.add('toast-leaving')"
></div>
</div>
</template>
CSS
.toast {
display: flex;
flex-direction: column;
border: solid 1px black;
width: 300px;
padding: 0.5rem;
cursor: default;
background-color: whitesmoke;
animation-play-state: running;
}
.toast-progress {
background-color: blue;
height: 4px;
}
.toast-entering {
animation: toast-in 280ms ease-out both;
}
.toast-leaving {
animation: toast-out 160ms ease-in both;
}
.toast-progress {
width: 100%;
transform-origin: left center;
animation: toast-progress var(--toast-duration) linear both;
}
@keyframes toast-progress {
from {
transform: scaleX(1);
}
to {
transform: scaleX(0);
}
}
@keyframes toast-in {
from {
opacity: 0.25;
transform: translateY(-30px);
}
to {
opacity: 1;
transform: translateY(0);
}
}
@keyframes toast-out {
from {
opacity: 1;
transform: translateY(0);
}
to {
opacity: 0.25;
transform: translateY(-30px);
}
}
.toast-container {
position: fixed;
top: 2rem;
right: 2rem;
display: flex;
flex-direction: column;
gap: 0.25rem;
}
.toast-container:hover .toast,
.toast-container:hover .toast-progress {
animation-play-state: paused;
}
.toast-container:hover .toast.toast-entering,
.toast-container:hover .toast.toast-leaving,
.toast-container:hover .toast.toast-entering .toast-progress,
.toast-container:hover .toast.toast-leaving .toast-progress {
animation-play-state: running;
}