Tempa UI

Toast Notification

Fixed-position notification with auto-dismiss and multiple variants.

Code

components/ToastNotification.astro
    
      ---interface Props {  position?: "top-right" | "top-left" | "bottom-right" | "bottom-left" | "top-center" | "bottom-center";  maxToasts?: number;  class?: string;} const {  position = "top-right",  maxToasts = 5,  class: className = "",} = Astro.props; const positionClasses = {  "top-right": "top-4 right-4",  "top-left": "top-4 left-4",  "bottom-right": "bottom-4 right-4",  "bottom-left": "bottom-4 left-4",  "top-center": "top-4 left-1/2 -translate-x-1/2",  "bottom-center": "bottom-4 left-1/2 -translate-x-1/2",};--- <div  id="toast-container"  class={`fixed z-50 flex flex-col gap-2 pointer-events-none ${positionClasses[position]} ${className}`}  role="status"  aria-live="polite"></div> <script define:vars={{ maxToasts }}>  var container = document.getElementById("toast-container");  var max = maxToasts;   var variantClasses = {    solid: "bg-black-onyx text-white-ghost",    "left-bordered": "bg-white-ghost border-l-4 border-black-onyx text-black-onyx shadow-lg shadow-black-onyx/10",  };   window.showToast = function (opts) {    var message = opts.message;    var variant = opts.variant || "solid";    var duration = opts.duration !== undefined ? opts.duration : 5000;    var id = "toast-" + Date.now() + "-" + Math.random().toString(36).slice(2, 6);     while (container.children.length >= max) {      var first = container.firstChild;      if (first) closeToast(first, true);    }     var toast = document.createElement("div");    toast.id = id;    toast.className = "flex items-start gap-3 px-4 py-3 rounded text-sm pointer-events-auto transition-all duration-300 translate-y-2 opacity-0 " + variantClasses[variant];    toast.setAttribute("role", "alert");    toast.innerHTML =      '<div class="flex-1">' + message + '</div>' +      '<button class="toast-close shrink-0 text-current/60 hover:text-current transition-colors cursor-pointer" aria-label="Close">' +        '<svg aria-hidden="true" xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" viewBox="0 0 256 256">' +          '<path d="M205.66,194.34a8,8,0,0,1-11.32,11.32L128,139.31,61.66,205.66a8,8,0,0,1-11.32-11.32L116.69,128,50.34,61.66A8,8,0,0,1,61.66,50.34L128,116.69l66.34-66.35a8,8,0,0,1,11.32,11.32L139.31,128Z"></path>' +        '</svg>' +      '</button>';     container.appendChild(toast);     requestAnimationFrame(function () {      toast.classList.remove("translate-y-2", "opacity-0");    });     toast.querySelector(".toast-close").addEventListener("click", function () {      closeToast(toast);    });     if (duration > 0) {      setTimeout(function () { closeToast(toast); }, duration);    }  };   function closeToast(toast, immediate) {    toast.classList.add("translate-y-2", "opacity-0");    if (immediate) {      toast.remove();    } else {      setTimeout(function () { toast.remove(); }, 300);    }  }</script> 
    
  

Preview

Solid Toast

Left Bordered Toast

Long Duration Toast

Usage

Place the ToastNotification component once in your layout, then call showToast() from anywhere.

    
      <ToastNotification position="top-right" />
    
  

Solid Variant

    
      <button onclick="showToast({ message: 'Component code copied to clipboard!', variant: 'solid', duration: 4000 })">  Show Solid Toast</button>
    
  

Bordered Variant

    
      <button onclick="showToast({ message: 'Your settings have been saved successfully.', variant: 'left-bordered', duration: 4000 })">  Show Left Bordered Toast</button>
    
  

Props

Prop Type Default Description
position "top-right" | "top-left" | "bottom-right" | "bottom-left" | "top-center" | "bottom-center" "top-right" Toast container position.
maxToasts number 5 Maximum visible toasts at once.
class string Additional CSS classes.

showToast Options

Option Type Default Description
message string Toast message text.
variant "solid" | "left-bordered" "solid" Toast style variant.
duration number 5000 Auto-dismiss in ms (0 = persistent).