A modern, animated notification toast system with multiple styles
<head>
.<!-- Font Awesome Icons; Make sure to add it in the <head> tag -->
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css">
<!-- Toast container where notifications will appear -->
<div class="toast-container"></div>
<!-- Example buttons to trigger different toast types -->
<button onclick="showToast('success', 'Success!', 'Operation completed successfully.')">Success</button>
<button onclick="showToast('error', 'Error!', 'Something went wrong.')">Error</button>
<button onclick="showToast('info', 'Info', 'Here is some information.')">Info</button>
<button onclick="showToast('warning', 'Warning', 'Please be careful.')">Warning</button>
.toast-container {
position: fixed;
top: 20px;
right: 20px;
z-index: 9999;
display: flex;
flex-direction: column;
gap: 10px;
}
.toast {
display: flex;
align-items: center;
min-width: 300px;
max-width: 400px;
background-color: white;
color: #333;
border-radius: 8px;
padding: 16px;
margin-bottom: 8px;
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);
transform: translateX(120%);
transition: transform 0.3s ease;
overflow: hidden;
position: relative;
}
.toast.show {
transform: translateX(0);
}
.toast-icon {
margin-right: 12px;
font-size: 20px;
min-width: 24px;
display: flex;
align-items: center;
justify-content: center;
}
.toast-content {
flex-grow: 1;
padding-right: 10px;
}
.toast-title {
font-weight: 600;
margin-bottom: 4px;
font-size: 16px;
}
.toast-message {
font-size: 14px;
color: #666;
}
.toast-close {
background: none;
border: none;
color: #888;
cursor: pointer;
font-size: 16px;
padding: 4px;
transition: color 0.2s;
}
.toast-close:hover {
color: #333;
}
.toast-progress {
position: absolute;
bottom: 0;
left: 0;
height: 4px;
width: 100%;
transform-origin: left;
}
/* Toast types */
.toast.success {
border-left: 4px solid #4caf50;
}
.toast.success .toast-icon {
color: #4caf50;
}
.toast.success .toast-progress {
background-color: #4caf50;
}
.toast.error {
border-left: 4px solid #f44336;
}
.toast.error .toast-icon {
color: #f44336;
}
.toast.error .toast-progress {
background-color: #f44336;
}
.toast.info {
border-left: 4px solid #2196f3;
}
.toast.info .toast-icon {
color: #2196f3;
}
.toast.info .toast-progress {
background-color: #2196f3;
}
.toast.warning {
border-left: 4px solid #ff9800;
}
.toast.warning .toast-icon {
color: #ff9800;
}
.toast.warning .toast-progress {
background-color: #ff9800;
}
/* Dark mode styling */
@media (prefers-color-scheme: dark) {
.toast {
background-color: #333;
color: #eee;
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.3);
}
.toast-message {
color: #bbb;
}
.toast-close {
color: #aaa;
}
.toast-close:hover {
color: #eee;
}
}
/**
* Show a toast notification
* @param {string} type - Type of toast: 'success', 'error', 'info', 'warning'
* @param {string} title - Toast title
* @param {string} message - Toast message
* @param {number} duration - Duration in milliseconds (default: 5000ms)
*/
function showToast(type, title, message, duration = 5000) {
// Create toast element
const toast = document.createElement('div');
toast.className = `toast ${type}`;
// Set icon based on type
let icon = '';
switch(type) {
case 'success':
icon = 'fa-circle-check';
break;
case 'error':
icon = 'fa-circle-xmark';
break;
case 'info':
icon = 'fa-circle-info';
break;
case 'warning':
icon = 'fa-triangle-exclamation';
break;
default:
icon = 'fa-bell';
}
// Create toast HTML structure
toast.innerHTML = `
<div class="toast-icon">
<i class="fas ${icon}"></i>
</div>
<div class="toast-content">
<div class="toast-title">${title}</div>
<div class="toast-message">${message}</div>
</div>
<button class="toast-close">
<i class="fas fa-times"></i>
</button>
<div class="toast-progress"></div>
`;
// Get the container
const container = document.querySelector('.toast-container');
// If container doesn't exist, create it
if (!container) {
const newContainer = document.createElement('div');
newContainer.className = 'toast-container';
document.body.appendChild(newContainer);
newContainer.appendChild(toast);
} else {
container.appendChild(toast);
}
// Add closing functionality
const closeButton = toast.querySelector('.toast-close');
closeButton.addEventListener('click', () => {
closeToast(toast);
});
// Animate progress bar
const progress = toast.querySelector('.toast-progress');
progress.style.animation = `progress-shrink ${duration/1000}s linear forwards`;
// Set timeout to remove toast
setTimeout(() => {
closeToast(toast);
}, duration);
// Make the toast appear after a short delay (for animation)
setTimeout(() => {
toast.classList.add('show');
}, 10);
}
/**
* Close and remove a toast
* @param {HTMLElement} toast - The toast element to close
*/
function closeToast(toast) {
toast.classList.remove('show');
// Wait for the animation to finish before removing
setTimeout(() => {
if (toast.parentElement) {
toast.parentElement.removeChild(toast);
}
}, 300);
}
// Add CSS keyframes for progress bar
const style = document.createElement('style');
style.textContent = `
@keyframes progress-shrink {
0% { transform: scaleX(1); }
100% { transform: scaleX(0); }
}
`;
document.head.appendChild(style);