Bulma Toast

A lightweight, animated toast notification extension for Bulma.

The Toast Extension provides a fixed-position container for stacking animated notification toasts. Toasts slide in from the edge of the screen, support all four corners, and use Bulma's .notification classes for color and styling.

Basic Usage

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);

Positions

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>

Color Variants

Toast items accept all Bulma .notification color modifiers.

Scripting

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;
    });
});