A lightweight, animated toast notification extension for Bulma.
Place a .toast-container anywhere in the page — it uses
position: fixed. Set a default auto-dismiss duration (ms) with
data-duration. Then call appendMessage() on the
registered toaster instance.
<!-- Place the container anywhere in the page -->
<div id="my-toaster" class="toast-container" data-duration="3000"></div> // Append a message using the container's default duration
window.toasters['my-toaster'].appendMessage('Operation completed!', 'is-success');
// Override with a custom duration (ms)
window.toasters['my-toaster'].appendMessage('Quick alert!', 'is-warning', 1500);
// Sticky toast (duration 0 — close manually via the × button)
window.toasters['my-toaster'].appendMessage('Sticky message.', 'is-info', 0);
Use .is-top-right, .is-top-left, or .is-bottom-left
on the container. The default (no modifier) positions toasts at the bottom-right.
<div id="toaster-br" class="toast-container" data-duration="3000"></div>
<div id="toaster-tr" class="toast-container is-top-right" data-duration="3000"></div>
<div id="toaster-tl" class="toast-container is-top-left" data-duration="3000"></div>
<div id="toaster-bl" class="toast-container is-bottom-left" data-duration="3000"></div>
Toast items accept all Bulma .notification color modifiers.
Scripting is needed to manage the toast messages, it can be done with a simple function or a component model.
class BulmaMessage {
constructor(node, duration) {
this.message = node;
this.duration = duration;
this.button = this.message.querySelector('.delete');
this.closeHandler = () => this.closeMessage();
this.init();
}
init() {
if (this.button) this.button.addEventListener('click', this.closeHandler);
if ((this.duration) && (this.duration > 0 ))
{
setTimeout(this.closeHandler, this.duration);
}
this.message.classList.toggle('is-visible');
}
closeMessage() {
this.message.classList.toggle('is-visible');
setTimeout(() => this.destroy(), 1000);
}
destroy() {
this.message.removeEventListener('click', this.closeHandler);
this.message.remove();
}
}
class BulmaToast {
constructor(node) {
this.toast = node;
this.defaultDuration = this.toast.dataset.duration;
if (!(this.defaultDuration)) {
this.defaultDuration = 0;
}
}
appendMessage(message, colorClass, duration) {
const item = document.createElement('div');
item.className = `toast-item notification ${colorClass}`;
item.innerHTML = `<button class="delete"></button>${message}`;
this.toast.appendChild(item);
let realDuration = this.defaultDuration;
if (duration) {
realDuration = duration;
}
new BulmaMessage(item, realDuration);
}
}
document.addEventListener('DOMContentLoaded', () => {
window.toasters = {};
document.querySelectorAll('.toast-container').forEach(n => {
const toast = new BulmaToast(n);
window.toasters[n.id] = toast;
});
});