๐Ÿž Projects

Build Modals and Toast Notifications โ€” The Right Way

๐Ÿ“… Jul 5, 2026 โฑ 3 min read

Modals ask, toasts tell. Both done right, in a handful of lines.

Modal โ€” use the platform

The <dialog> element handles focus trap, Esc and backdrop (full guide). The missing 10% is polish:

dialog { opacity: 0; translate: 0 12px; transition: .2s; }
dialog[open] { opacity: 1; translate: 0 0; }
@starting-style { dialog[open] { opacity: 0; translate: 0 12px; } }

Toast system โ€” the queue pattern

const stack = document.querySelector(".toast-stack");   // fixed, bottom-right

function toast(msg, type = "info", ms = 3000) {
  const el = document.createElement("div");
  el.className = `toast toast-${type}`;
  el.role = "status";                                    // screen readers announce it
  el.innerHTML = `${msg} <button aria-label="Dismiss">โœ•</button>`;
  el.querySelector("button").onclick = () => el.remove();
  stack.append(el);                                      // stacks automatically
  setTimeout(() => {
    el.classList.add("leaving");
    el.addEventListener("transitionend", () => el.remove());
  }, ms);
}

toast("Saved โœ“", "success");
toast("Network error โ€” retrying", "error", 5000);
.toast-stack { position: fixed; bottom: 20px; right: 20px;
  display: flex; flex-direction: column; gap: 8px; z-index: 999; }
.toast { animation: slideIn .25s ease; }
.toast.leaving { opacity: 0; translate: 20px 0; transition: .25s; }

Rules: toasts never require action (that's a modal's job), errors stay longer than successes, and role="status" makes them accessible. Our playground's "Copied!" toast is this exact pattern.

โ† All Articles