diff --git a/webui/src/common/components/Toast/Toast.jsx b/webui/src/common/components/Toast/Toast.jsx
new file mode 100644
index 0000000..3bc8aed
--- /dev/null
+++ b/webui/src/common/components/Toast/Toast.jsx
@@ -0,0 +1,85 @@
+import React, { useEffect, useState } from 'react';
+import {
+ XIcon,
+ CheckCircleIcon,
+ XCircleIcon,
+ WarningIcon,
+ InfoIcon
+} from '@phosphor-icons/react';
+import Button from '@/common/components/Button';
+import './styles.sass';
+
+const TOAST_ICONS = {
+ success: ,
+ error: ,
+ warning: ,
+ info:
+};
+
+export const Toast = ({
+ id,
+ type = 'info',
+ message,
+ title,
+ duration = 5000,
+ onClose,
+ actions
+}) => {
+ const [isVisible, setIsVisible] = useState(false);
+ const [isRemoving, setIsRemoving] = useState(false);
+
+ useEffect(() => {
+ const timer = setTimeout(() => setIsVisible(true), 10);
+ return () => clearTimeout(timer);
+ }, []);
+
+ const handleClose = () => {
+ setIsRemoving(true);
+ setTimeout(() => {
+ onClose?.();
+ }, 150);
+ };
+
+ const toastClasses = [
+ 'toast',
+ `toast--${type}`,
+ isVisible && 'toast--visible',
+ isRemoving && 'toast--removing'
+ ].filter(Boolean).join(' ');
+
+ return (
+
+
+ {TOAST_ICONS[type]}
+
+
+
+ {title &&
{title}
}
+
{message}
+
+ {actions && (
+
+ {actions.map((action, index) => (
+
+ ))}
+
+ )}
+
+
+
}
+ onClick={handleClose}
+ className="toast-close"
+ />
+
+ );
+};
\ No newline at end of file
diff --git a/webui/src/common/components/Toast/index.js b/webui/src/common/components/Toast/index.js
new file mode 100644
index 0000000..c06480c
--- /dev/null
+++ b/webui/src/common/components/Toast/index.js
@@ -0,0 +1 @@
+export { Toast as default } from './Toast.jsx';
\ No newline at end of file
diff --git a/webui/src/common/components/Toast/styles.sass b/webui/src/common/components/Toast/styles.sass
new file mode 100644
index 0000000..bb4029b
--- /dev/null
+++ b/webui/src/common/components/Toast/styles.sass
@@ -0,0 +1,92 @@
+.toast-container
+ position: fixed
+ top: 1rem
+ right: 1rem
+ z-index: 2000
+ display: flex
+ flex-direction: column
+ gap: 0.75rem
+ max-width: 400px
+ pointer-events: none
+
+.toast
+ background: var(--bg-alt)
+ border: 1px solid var(--border)
+ border-radius: var(--radius-lg)
+ box-shadow: 0 10px 40px rgba(0, 0, 0, 0.15)
+ padding: 1rem
+ display: flex
+ gap: 0.75rem
+ align-items: flex-start
+ min-width: 300px
+ pointer-events: auto
+ transform: translateX(100%)
+ opacity: 0
+ transition: all 0.15s ease-out
+
+ &--visible
+ transform: translateX(0)
+ opacity: 1
+
+ &--removing
+ transform: translateX(100%)
+ opacity: 0
+
+ &--success
+ border-left: 4px solid #16a34a
+ .toast-icon
+ color: #16a34a
+
+ &--error
+ border-left: 4px solid #d93025
+ .toast-icon
+ color: #d93025
+
+ &--warning
+ border-left: 4px solid #f59e0b
+ .toast-icon
+ color: #f59e0b
+
+ &--info
+ border-left: 4px solid #0f62fe
+ .toast-icon
+ color: #0f62fe
+
+.toast-icon
+ flex-shrink: 0
+ margin-top: 0.125rem
+
+.toast-content
+ flex: 1
+ min-width: 0
+
+.toast-title
+ font-weight: 600
+ color: var(--text)
+ margin-bottom: 0.25rem
+ font-size: 0.9rem
+
+.toast-message
+ color: var(--text-dim)
+ font-size: 0.85rem
+ line-height: 1.4
+ word-wrap: break-word
+
+.toast-actions
+ display: flex
+ gap: 0.5rem
+ margin-top: 0.75rem
+
+.toast-close
+ flex-shrink: 0
+ margin-left: 0.5rem
+
+@media (max-width: 768px)
+ .toast-container
+ left: 1rem
+ right: 1rem
+ top: 1rem
+ max-width: none
+
+ .toast
+ min-width: auto
\ No newline at end of file