CSS Banners & Alerts 07 / 14
CSS Toast Notification Alert Top Right
A top-right CSS toast notification system in a soft glassmorphism / frosted UI aesthetic — purple-blue ambient orbs, translucent frosted cards, spring-physics slide-in from right, and a progress bar that shrinks via <code>scaleX</code> as the auto-dismiss timer runs.
Best forSaaS dashboards, web-app feedback, async operations confirmation, modern notification systems.
The code
<section class="ba-tst" aria-label="Toast notification demo">
<div class="panel">
<div class="panel-title">Trigger a toast</div>
<div class="btn-grid">
<button class="trigger-btn trigger-btn--success" type="button" data-ba-tst-trigger="success"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><polyline points="20 6 9 17 4 12"/></svg> Success</button>
<button class="trigger-btn trigger-btn--error" type="button" data-ba-tst-trigger="error"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" aria-hidden="true"><circle cx="12" cy="12" r="10"/><line x1="15" y1="9" x2="9" y2="15"/><line x1="9" y1="9" x2="15" y2="15"/></svg> Error</button>
<button class="trigger-btn trigger-btn--warning" type="button" data-ba-tst-trigger="warning"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M10.29 3.86L1.82 18a2 2 0 001.71 3h16.94a2 2 0 001.71-3L13.71 3.86a2 2 0 00-3.42 0z"/><line x1="12" y1="9" x2="12" y2="13"/></svg> Warning</button>
<button class="trigger-btn trigger-btn--info" type="button" data-ba-tst-trigger="info"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" aria-hidden="true"><circle cx="12" cy="12" r="10"/><line x1="12" y1="16" x2="12" y2="12"/><line x1="12" y1="8" x2="12.01" y2="8"/></svg> Info</button>
<button class="trigger-btn trigger-btn--neutral" type="button" data-ba-tst-trigger="neutral"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" aria-hidden="true"><rect x="3" y="3" width="18" height="18" rx="2"/></svg> Default</button>
<button class="trigger-btn trigger-btn--promise" type="button" data-ba-tst-trigger="promise"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><polyline points="23 4 23 10 17 10"/><path d="M20.49 15a9 9 0 11-2.12-9.36L23 10"/></svg> Promise (async)</button>
</div>
</div>
<div class="toast-container" data-ba-tst-container aria-live="polite" aria-atomic="false"></div>
</section> <section class="ba-tst" aria-label="Toast notification demo">
<div class="panel">
<div class="panel-title">Trigger a toast</div>
<div class="btn-grid">
<button class="trigger-btn trigger-btn--success" type="button" data-ba-tst-trigger="success"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><polyline points="20 6 9 17 4 12"/></svg> Success</button>
<button class="trigger-btn trigger-btn--error" type="button" data-ba-tst-trigger="error"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" aria-hidden="true"><circle cx="12" cy="12" r="10"/><line x1="15" y1="9" x2="9" y2="15"/><line x1="9" y1="9" x2="15" y2="15"/></svg> Error</button>
<button class="trigger-btn trigger-btn--warning" type="button" data-ba-tst-trigger="warning"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M10.29 3.86L1.82 18a2 2 0 001.71 3h16.94a2 2 0 001.71-3L13.71 3.86a2 2 0 00-3.42 0z"/><line x1="12" y1="9" x2="12" y2="13"/></svg> Warning</button>
<button class="trigger-btn trigger-btn--info" type="button" data-ba-tst-trigger="info"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" aria-hidden="true"><circle cx="12" cy="12" r="10"/><line x1="12" y1="16" x2="12" y2="12"/><line x1="12" y1="8" x2="12.01" y2="8"/></svg> Info</button>
<button class="trigger-btn trigger-btn--neutral" type="button" data-ba-tst-trigger="neutral"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" aria-hidden="true"><rect x="3" y="3" width="18" height="18" rx="2"/></svg> Default</button>
<button class="trigger-btn trigger-btn--promise" type="button" data-ba-tst-trigger="promise"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><polyline points="23 4 23 10 17 10"/><path d="M20.49 15a9 9 0 11-2.12-9.36L23 10"/></svg> Promise (async)</button>
</div>
</div>
<div class="toast-container" data-ba-tst-container aria-live="polite" aria-atomic="false"></div>
</section>/* ─── 07 Toast Notification — glassmorphism top-right ─────────── */
@import url('https://fonts.googleapis.com/css2?family=Plus+Jakarta+Sans:wght@400;500;600;700&family=JetBrains+Mono:wght@400;500&display=swap');
.ba-tst {
--ba-tst-bg-from: #e0d7ff;
--ba-tst-bg-to: #c8e6ff;
--ba-tst-glass: rgba(255,255,255,0.55);
--ba-tst-glass-border: rgba(255,255,255,0.75);
--ba-tst-shadow: rgba(100,80,180,0.18);
--ba-tst-ink: #1a1630;
--ba-tst-muted: rgba(26,22,48,0.5);
--ba-tst-c-success: #22c55e;
--ba-tst-c-error: #ef4444;
--ba-tst-c-warning: #f59e0b;
--ba-tst-c-info: #6366f1;
--ba-tst-c-neutral: #64748b;
position: relative;
width: 100%;
min-height: 540px;
background: linear-gradient(135deg, var(--ba-tst-bg-from) 0%, var(--ba-tst-bg-to) 60%, #d4f0ff 100%);
font-family: 'Plus Jakarta Sans', sans-serif;
display: flex; align-items: center; justify-content: center;
padding: 60px 20px 40px;
overflow: hidden;
box-sizing: border-box;
}
.ba-tst *, .ba-tst *::before, .ba-tst *::after { box-sizing: border-box; margin: 0; padding: 0; }
.ba-tst::before, .ba-tst::after { content: ''; position: absolute; border-radius: 50%; pointer-events: none; filter: blur(80px); z-index: 0; }
.ba-tst::before { width: 520px; height: 520px; background: rgba(139,92,246,0.22); top: -100px; left: -120px; }
.ba-tst::after { width: 400px; height: 400px; background: rgba(59,130,246,0.18); bottom: -80px; right: -80px; }
.ba-tst .panel { background: var(--ba-tst-glass); backdrop-filter: blur(20px) saturate(1.4); -webkit-backdrop-filter: blur(20px) saturate(1.4); border: 1.5px solid var(--ba-tst-glass-border); border-radius: 20px; padding: 28px 32px; width: 100%; max-width: 420px; box-shadow: 0 8px 40px var(--ba-tst-shadow), inset 0 1px 0 rgba(255,255,255,0.8); z-index: 1; position: relative; }
.ba-tst .panel-title { font-size: 13px; font-weight: 700; letter-spacing: 0.12em; text-transform: uppercase; color: var(--ba-tst-muted); margin-bottom: 16px; }
.ba-tst .btn-grid { display: grid; grid-template-columns: 1fr 1fr; gap: 10px; }
.ba-tst .trigger-btn { font-family: 'Plus Jakarta Sans', sans-serif; font-size: 12.5px; font-weight: 600; padding: 11px 14px; border-radius: 12px; border: 1.5px solid transparent; cursor: pointer; transition: all 0.2s ease; display: flex; align-items: center; gap: 8px; color: white; }
.ba-tst .trigger-btn svg { width: 15px; height: 15px; flex-shrink: 0; }
.ba-tst .trigger-btn--success { background: var(--ba-tst-c-success); }
.ba-tst .trigger-btn--error { background: var(--ba-tst-c-error); }
.ba-tst .trigger-btn--warning { background: var(--ba-tst-c-warning); }
.ba-tst .trigger-btn--info { background: var(--ba-tst-c-info); }
.ba-tst .trigger-btn--neutral { background: var(--ba-tst-c-neutral); }
.ba-tst .trigger-btn--promise { background: linear-gradient(135deg, #6366f1, #8b5cf6); grid-column: span 2; justify-content: center; }
.ba-tst .trigger-btn:hover { opacity: 0.88; transform: translateY(-1px); box-shadow: 0 4px 16px rgba(0,0,0,0.15); }
.ba-tst .toast-container { position: absolute; top: 20px; right: 20px; z-index: 100; display: flex; flex-direction: column; gap: 10px; align-items: flex-end; pointer-events: none; }
.ba-tst .toast { pointer-events: all; width: 340px; max-width: calc(100% - 40px); background: rgba(255,255,255,0.72); backdrop-filter: blur(24px) saturate(1.8); -webkit-backdrop-filter: blur(24px) saturate(1.8); border: 1.5px solid rgba(255,255,255,0.85); border-radius: 16px; box-shadow: 0 4px 24px rgba(100,80,180,0.14), 0 1px 4px rgba(0,0,0,0.06); padding: 14px 14px 14px 16px; display: flex; align-items: flex-start; gap: 12px; position: relative; overflow: hidden; animation: ba-tst-in 0.4s cubic-bezier(0.34, 1.56, 0.64, 1) both; }
@keyframes ba-tst-in { from { opacity: 0; transform: translateX(60px) scale(0.92); } to { opacity: 1; transform: translateX(0) scale(1); } }
.ba-tst .toast.exiting { animation: ba-tst-out 0.32s cubic-bezier(0.4, 0, 1, 1) forwards; }
@keyframes ba-tst-out { 0% { opacity: 1; transform: translateX(0); max-height: 200px; margin-bottom: 0; } 60% { opacity: 0; transform: translateX(50px); } 100% { opacity: 0; max-height: 0; margin-bottom: -10px; padding: 0; } }
.ba-tst .toast::after { content: ''; position: absolute; bottom: 0; left: 0; height: 2.5px; background: var(--toast-color, #6366f1); width: 100%; animation: ba-tst-progress var(--toast-duration, 4000ms) linear forwards; transform-origin: left; }
@keyframes ba-tst-progress { from { transform: scaleX(1); } to { transform: scaleX(0); } }
.ba-tst .toast::before { content: ''; position: absolute; top: 0; left: 0; width: 4px; height: 100%; border-radius: 16px 0 0 16px; background: var(--toast-color, #6366f1); }
.ba-tst .toast-icon { flex-shrink: 0; width: 34px; height: 34px; border-radius: 10px; display: flex; align-items: center; justify-content: center; background: color-mix(in srgb, var(--toast-color, #6366f1) 12%, white); }
.ba-tst .toast-icon svg { width: 17px; height: 17px; color: var(--toast-color, #6366f1); }
.ba-tst .toast-body { flex: 1; min-width: 0; }
.ba-tst .toast-title { font-size: 13.5px; font-weight: 700; color: var(--ba-tst-ink); line-height: 1.25; margin-bottom: 2px; }
.ba-tst .toast-msg { font-size: 12px; color: var(--ba-tst-muted); line-height: 1.5; }
.ba-tst .toast-time { font-family: 'JetBrains Mono', monospace; font-size: 9.5px; color: rgba(26,22,48,0.3); margin-top: 5px; display: block; }
.ba-tst .toast-close { flex-shrink: 0; width: 24px; height: 24px; border: none; background: rgba(0,0,0,0.05); border-radius: 7px; cursor: pointer; display: flex; align-items: center; justify-content: center; color: var(--ba-tst-muted); transition: background 0.2s; margin-top: 1px; }
.ba-tst .toast-close svg { width: 12px; height: 12px; }
.ba-tst .toast-loader { display: flex; gap: 4px; margin-top: 6px; }
.ba-tst .toast-loader span { width: 5px; height: 5px; border-radius: 50%; background: var(--toast-color, #6366f1); opacity: 0.3; animation: ba-tst-dot 1.2s ease-in-out infinite; }
.ba-tst .toast-loader span:nth-child(2) { animation-delay: 0.2s; }
.ba-tst .toast-loader span:nth-child(3) { animation-delay: 0.4s; }
@keyframes ba-tst-dot { 0%,80%,100% { opacity: 0.3; transform: scale(1); } 40% { opacity: 1; transform: scale(1.4); } }
@media (prefers-reduced-motion: reduce) { .ba-tst .toast, .ba-tst .toast::after, .ba-tst .toast-loader span { animation: none !important; } } /* ─── 07 Toast Notification — glassmorphism top-right ─────────── */
@import url('https://fonts.googleapis.com/css2?family=Plus+Jakarta+Sans:wght@400;500;600;700&family=JetBrains+Mono:wght@400;500&display=swap');
.ba-tst {
--ba-tst-bg-from: #e0d7ff;
--ba-tst-bg-to: #c8e6ff;
--ba-tst-glass: rgba(255,255,255,0.55);
--ba-tst-glass-border: rgba(255,255,255,0.75);
--ba-tst-shadow: rgba(100,80,180,0.18);
--ba-tst-ink: #1a1630;
--ba-tst-muted: rgba(26,22,48,0.5);
--ba-tst-c-success: #22c55e;
--ba-tst-c-error: #ef4444;
--ba-tst-c-warning: #f59e0b;
--ba-tst-c-info: #6366f1;
--ba-tst-c-neutral: #64748b;
position: relative;
width: 100%;
min-height: 540px;
background: linear-gradient(135deg, var(--ba-tst-bg-from) 0%, var(--ba-tst-bg-to) 60%, #d4f0ff 100%);
font-family: 'Plus Jakarta Sans', sans-serif;
display: flex; align-items: center; justify-content: center;
padding: 60px 20px 40px;
overflow: hidden;
box-sizing: border-box;
}
.ba-tst *, .ba-tst *::before, .ba-tst *::after { box-sizing: border-box; margin: 0; padding: 0; }
.ba-tst::before, .ba-tst::after { content: ''; position: absolute; border-radius: 50%; pointer-events: none; filter: blur(80px); z-index: 0; }
.ba-tst::before { width: 520px; height: 520px; background: rgba(139,92,246,0.22); top: -100px; left: -120px; }
.ba-tst::after { width: 400px; height: 400px; background: rgba(59,130,246,0.18); bottom: -80px; right: -80px; }
.ba-tst .panel { background: var(--ba-tst-glass); backdrop-filter: blur(20px) saturate(1.4); -webkit-backdrop-filter: blur(20px) saturate(1.4); border: 1.5px solid var(--ba-tst-glass-border); border-radius: 20px; padding: 28px 32px; width: 100%; max-width: 420px; box-shadow: 0 8px 40px var(--ba-tst-shadow), inset 0 1px 0 rgba(255,255,255,0.8); z-index: 1; position: relative; }
.ba-tst .panel-title { font-size: 13px; font-weight: 700; letter-spacing: 0.12em; text-transform: uppercase; color: var(--ba-tst-muted); margin-bottom: 16px; }
.ba-tst .btn-grid { display: grid; grid-template-columns: 1fr 1fr; gap: 10px; }
.ba-tst .trigger-btn { font-family: 'Plus Jakarta Sans', sans-serif; font-size: 12.5px; font-weight: 600; padding: 11px 14px; border-radius: 12px; border: 1.5px solid transparent; cursor: pointer; transition: all 0.2s ease; display: flex; align-items: center; gap: 8px; color: white; }
.ba-tst .trigger-btn svg { width: 15px; height: 15px; flex-shrink: 0; }
.ba-tst .trigger-btn--success { background: var(--ba-tst-c-success); }
.ba-tst .trigger-btn--error { background: var(--ba-tst-c-error); }
.ba-tst .trigger-btn--warning { background: var(--ba-tst-c-warning); }
.ba-tst .trigger-btn--info { background: var(--ba-tst-c-info); }
.ba-tst .trigger-btn--neutral { background: var(--ba-tst-c-neutral); }
.ba-tst .trigger-btn--promise { background: linear-gradient(135deg, #6366f1, #8b5cf6); grid-column: span 2; justify-content: center; }
.ba-tst .trigger-btn:hover { opacity: 0.88; transform: translateY(-1px); box-shadow: 0 4px 16px rgba(0,0,0,0.15); }
.ba-tst .toast-container { position: absolute; top: 20px; right: 20px; z-index: 100; display: flex; flex-direction: column; gap: 10px; align-items: flex-end; pointer-events: none; }
.ba-tst .toast { pointer-events: all; width: 340px; max-width: calc(100% - 40px); background: rgba(255,255,255,0.72); backdrop-filter: blur(24px) saturate(1.8); -webkit-backdrop-filter: blur(24px) saturate(1.8); border: 1.5px solid rgba(255,255,255,0.85); border-radius: 16px; box-shadow: 0 4px 24px rgba(100,80,180,0.14), 0 1px 4px rgba(0,0,0,0.06); padding: 14px 14px 14px 16px; display: flex; align-items: flex-start; gap: 12px; position: relative; overflow: hidden; animation: ba-tst-in 0.4s cubic-bezier(0.34, 1.56, 0.64, 1) both; }
@keyframes ba-tst-in { from { opacity: 0; transform: translateX(60px) scale(0.92); } to { opacity: 1; transform: translateX(0) scale(1); } }
.ba-tst .toast.exiting { animation: ba-tst-out 0.32s cubic-bezier(0.4, 0, 1, 1) forwards; }
@keyframes ba-tst-out { 0% { opacity: 1; transform: translateX(0); max-height: 200px; margin-bottom: 0; } 60% { opacity: 0; transform: translateX(50px); } 100% { opacity: 0; max-height: 0; margin-bottom: -10px; padding: 0; } }
.ba-tst .toast::after { content: ''; position: absolute; bottom: 0; left: 0; height: 2.5px; background: var(--toast-color, #6366f1); width: 100%; animation: ba-tst-progress var(--toast-duration, 4000ms) linear forwards; transform-origin: left; }
@keyframes ba-tst-progress { from { transform: scaleX(1); } to { transform: scaleX(0); } }
.ba-tst .toast::before { content: ''; position: absolute; top: 0; left: 0; width: 4px; height: 100%; border-radius: 16px 0 0 16px; background: var(--toast-color, #6366f1); }
.ba-tst .toast-icon { flex-shrink: 0; width: 34px; height: 34px; border-radius: 10px; display: flex; align-items: center; justify-content: center; background: color-mix(in srgb, var(--toast-color, #6366f1) 12%, white); }
.ba-tst .toast-icon svg { width: 17px; height: 17px; color: var(--toast-color, #6366f1); }
.ba-tst .toast-body { flex: 1; min-width: 0; }
.ba-tst .toast-title { font-size: 13.5px; font-weight: 700; color: var(--ba-tst-ink); line-height: 1.25; margin-bottom: 2px; }
.ba-tst .toast-msg { font-size: 12px; color: var(--ba-tst-muted); line-height: 1.5; }
.ba-tst .toast-time { font-family: 'JetBrains Mono', monospace; font-size: 9.5px; color: rgba(26,22,48,0.3); margin-top: 5px; display: block; }
.ba-tst .toast-close { flex-shrink: 0; width: 24px; height: 24px; border: none; background: rgba(0,0,0,0.05); border-radius: 7px; cursor: pointer; display: flex; align-items: center; justify-content: center; color: var(--ba-tst-muted); transition: background 0.2s; margin-top: 1px; }
.ba-tst .toast-close svg { width: 12px; height: 12px; }
.ba-tst .toast-loader { display: flex; gap: 4px; margin-top: 6px; }
.ba-tst .toast-loader span { width: 5px; height: 5px; border-radius: 50%; background: var(--toast-color, #6366f1); opacity: 0.3; animation: ba-tst-dot 1.2s ease-in-out infinite; }
.ba-tst .toast-loader span:nth-child(2) { animation-delay: 0.2s; }
.ba-tst .toast-loader span:nth-child(3) { animation-delay: 0.4s; }
@keyframes ba-tst-dot { 0%,80%,100% { opacity: 0.3; transform: scale(1); } 40% { opacity: 1; transform: scale(1.4); } }
@media (prefers-reduced-motion: reduce) { .ba-tst .toast, .ba-tst .toast::after, .ba-tst .toast-loader span { animation: none !important; } }(() => {
const root = document.querySelector('.ba-tst');
if (!root) return;
const container = root.querySelector('[data-ba-tst-container]');
if (!container) return;
const CONFIGS = {
success: { color: '#22c55e', title: 'Upload complete', msg: 'report_q2.pdf was saved to your drive.', icon: '<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round"><polyline points="20 6 9 17 4 12"/></svg>', duration: 4000 },
error: { color: '#ef4444', title: 'Payment failed', msg: 'Card ending in 4242 was declined.', icon: '<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round"><circle cx="12" cy="12" r="10"/><line x1="15" y1="9" x2="9" y2="15"/><line x1="9" y1="9" x2="15" y2="15"/></svg>', duration: 6000 },
warning: { color: '#f59e0b', title: 'Storage at 90%', msg: 'Free up space to avoid paused uploads.', icon: '<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round"><path d="M10.29 3.86L1.82 18a2 2 0 001.71 3h16.94a2 2 0 001.71-3L13.71 3.86a2 2 0 00-3.42 0z"/><line x1="12" y1="9" x2="12" y2="13"/></svg>', duration: 5000 },
info: { color: '#6366f1', title: 'New version available', msg: 'v5.2.0 is ready — see what is new.', icon: '<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round"><circle cx="12" cy="12" r="10"/><line x1="12" y1="16" x2="12" y2="12"/><line x1="12" y1="8" x2="12.01" y2="8"/></svg>', duration: 5000 },
neutral: { color: '#64748b', title: 'Changes auto-saved', msg: 'Last save 2 seconds ago.', icon: '<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round"><rect x="3" y="3" width="18" height="18" rx="2"/></svg>', duration: 3000 },
promise: { color: '#8b5cf6', title: 'Syncing workspace…', msg: 'This resolves automatically in 3 seconds.', icon: '<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round"><polyline points="23 4 23 10 17 10"/><path d="M20.49 15a9 9 0 11-2.12-9.36L23 10"/></svg>', duration: 3000, isPromise: true },
};
function now() {
return new Date().toLocaleTimeString('en-US', { hour: '2-digit', minute: '2-digit', second: '2-digit' });
}
function dismiss(el) {
if (!el) return;
if (el._baTstTimer) clearTimeout(el._baTstTimer);
el.classList.add('exiting');
el.addEventListener('animationend', () => el.remove(), { once: true });
}
function show(type) {
const cfg = CONFIGS[type];
if (!cfg) return;
const el = document.createElement('div');
el.className = 'toast';
el.style.setProperty('--toast-color', cfg.color);
el.style.setProperty('--toast-duration', cfg.duration + 'ms');
el.innerHTML =
'<div class="toast-icon">' + cfg.icon + '</div>' +
'<div class="toast-body">' +
'<div class="toast-title">' + cfg.title + '</div>' +
'<div class="toast-msg">' + cfg.msg + '</div>' +
(cfg.isPromise ? '<div class="toast-loader"><span></span><span></span><span></span></div>' : '') +
'<span class="toast-time">' + now() + '</span>' +
'</div>' +
'<button class="toast-close" type="button" aria-label="Dismiss"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round"><line x1="18" y1="6" x2="6" y2="18"/><line x1="6" y1="6" x2="18" y2="18"/></svg></button>';
container.prepend(el);
el.querySelector('.toast-close').addEventListener('click', () => dismiss(el));
el._baTstTimer = setTimeout(() => {
if (cfg.isPromise) {
el.querySelector('.toast-title').textContent = 'Sync complete!';
el.querySelector('.toast-msg').textContent = 'All files are up to date.';
const loader = el.querySelector('.toast-loader'); if (loader) loader.remove();
el.style.setProperty('--toast-color', '#22c55e');
el.querySelector('.toast-icon').innerHTML = '<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round"><polyline points="20 6 9 17 4 12"/></svg>';
setTimeout(() => dismiss(el), 3000);
} else {
dismiss(el);
}
}, cfg.duration);
}
root.addEventListener('click', (e) => {
const btn = e.target.closest('[data-ba-tst-trigger]');
if (btn) show(btn.dataset.baTstTrigger);
});
})(); (() => {
const root = document.querySelector('.ba-tst');
if (!root) return;
const container = root.querySelector('[data-ba-tst-container]');
if (!container) return;
const CONFIGS = {
success: { color: '#22c55e', title: 'Upload complete', msg: 'report_q2.pdf was saved to your drive.', icon: '<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round"><polyline points="20 6 9 17 4 12"/></svg>', duration: 4000 },
error: { color: '#ef4444', title: 'Payment failed', msg: 'Card ending in 4242 was declined.', icon: '<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round"><circle cx="12" cy="12" r="10"/><line x1="15" y1="9" x2="9" y2="15"/><line x1="9" y1="9" x2="15" y2="15"/></svg>', duration: 6000 },
warning: { color: '#f59e0b', title: 'Storage at 90%', msg: 'Free up space to avoid paused uploads.', icon: '<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round"><path d="M10.29 3.86L1.82 18a2 2 0 001.71 3h16.94a2 2 0 001.71-3L13.71 3.86a2 2 0 00-3.42 0z"/><line x1="12" y1="9" x2="12" y2="13"/></svg>', duration: 5000 },
info: { color: '#6366f1', title: 'New version available', msg: 'v5.2.0 is ready — see what is new.', icon: '<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round"><circle cx="12" cy="12" r="10"/><line x1="12" y1="16" x2="12" y2="12"/><line x1="12" y1="8" x2="12.01" y2="8"/></svg>', duration: 5000 },
neutral: { color: '#64748b', title: 'Changes auto-saved', msg: 'Last save 2 seconds ago.', icon: '<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round"><rect x="3" y="3" width="18" height="18" rx="2"/></svg>', duration: 3000 },
promise: { color: '#8b5cf6', title: 'Syncing workspace…', msg: 'This resolves automatically in 3 seconds.', icon: '<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round"><polyline points="23 4 23 10 17 10"/><path d="M20.49 15a9 9 0 11-2.12-9.36L23 10"/></svg>', duration: 3000, isPromise: true },
};
function now() {
return new Date().toLocaleTimeString('en-US', { hour: '2-digit', minute: '2-digit', second: '2-digit' });
}
function dismiss(el) {
if (!el) return;
if (el._baTstTimer) clearTimeout(el._baTstTimer);
el.classList.add('exiting');
el.addEventListener('animationend', () => el.remove(), { once: true });
}
function show(type) {
const cfg = CONFIGS[type];
if (!cfg) return;
const el = document.createElement('div');
el.className = 'toast';
el.style.setProperty('--toast-color', cfg.color);
el.style.setProperty('--toast-duration', cfg.duration + 'ms');
el.innerHTML =
'<div class="toast-icon">' + cfg.icon + '</div>' +
'<div class="toast-body">' +
'<div class="toast-title">' + cfg.title + '</div>' +
'<div class="toast-msg">' + cfg.msg + '</div>' +
(cfg.isPromise ? '<div class="toast-loader"><span></span><span></span><span></span></div>' : '') +
'<span class="toast-time">' + now() + '</span>' +
'</div>' +
'<button class="toast-close" type="button" aria-label="Dismiss"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round"><line x1="18" y1="6" x2="6" y2="18"/><line x1="6" y1="6" x2="18" y2="18"/></svg></button>';
container.prepend(el);
el.querySelector('.toast-close').addEventListener('click', () => dismiss(el));
el._baTstTimer = setTimeout(() => {
if (cfg.isPromise) {
el.querySelector('.toast-title').textContent = 'Sync complete!';
el.querySelector('.toast-msg').textContent = 'All files are up to date.';
const loader = el.querySelector('.toast-loader'); if (loader) loader.remove();
el.style.setProperty('--toast-color', '#22c55e');
el.querySelector('.toast-icon').innerHTML = '<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round"><polyline points="20 6 9 17 4 12"/></svg>';
setTimeout(() => dismiss(el), 3000);
} else {
dismiss(el);
}
}, cfg.duration);
}
root.addEventListener('click', (e) => {
const btn = e.target.closest('[data-ba-tst-trigger]');
if (btn) show(btn.dataset.baTstTrigger);
});
})();More from CSS Banners & Alerts
CSS Alert Banner with Icon AlignmentCSS Banner with Diagonal Slash BackgroundCSS Gradient Animated Border AlertSemantic Bootstrap-style Alerts CSSInline Form Validation Alert CSSCSS Pulsing Banner for Live UpdatesMinimalist Border Left CSS AlertResponsive Full Width Hero Banner CSSMaterial Design Floating Alert CardCSS Text Wrap Banner with Long StringsDismissible CSS Alert with Close ButtonSticky Top Announcement Banner CSS
View the full collection →