music on atproto
plyr.fm
toast notification system#
status#
IMPLEMENTED - this feature is live in production
overview#
lightweight, zero-dependency toast notification system providing immediate user feedback for async operations (file uploads, network errors, etc.).
use cases#
the toast system provides UX feedback for:
- uploads: "uploading track... 45%" → "track uploaded successfully!"
- upload errors: detailed error messages with specific failure reasons
- network errors: "network error: connection failed"
- processing updates: real-time SSE progress updates during transcoding
- general notifications: success/error/info/warning messages throughout the app
implementation#
state management#
uses svelte 5 class with $state runes (consistent with player.svelte.ts and other state managers):
// frontend/src/lib/toast.svelte.ts
class ToastState {
toasts = $state<Toast[]>([]);
add(message: string, type: ToastType = 'info', duration = 3000): string
dismiss(id: string): void
update(id: string, message: string, type?: ToastType): void
success(message: string, duration = 3000): string
error(message: string, duration = 5000): string
info(message: string, duration = 3000): string
warning(message: string, duration = 4000): string
}
export const toast = new ToastState();
toast types and timing#
- success: 3s auto-dismiss, ✓ icon, green accent
- error: 5s auto-dismiss, ✕ icon, red accent
- info: 3s auto-dismiss, ℹ icon, blue accent
- warning: 4s auto-dismiss, ⚠ icon, orange accent
- custom duration supported:
toast.add('message', 'info', 10000)
visual design#
- dark background with glassmorphism (backdrop-filter blur)
- type-specific icon colors using CSS variables
- positioned bottom-left (above player on mobile)
- fade transitions (respects
prefers-reduced-motion)
positioning#
all devices: bottom-left corner
- positioned above player footer using
calc(var(--player-height) + 1rem) - doesn't block main content
- stacks vertically with newest on top
- responsive padding adjustments for mobile
accessibility#
implemented features:
role="region"+aria-live="polite"on containerrole="alert"on individual toastsaria-hidden="true"on decorative icons- respects
prefers-reduced-motionmedia query - auto-dismiss ensures toasts don't linger indefinitely
usage patterns#
simple notifications#
import { toast } from '$lib/toast.svelte';
toast.success('track uploaded');
toast.error('upload failed');
toast.info('processing...');
toast.warning('approaching rate limit');
progress updates (uploader pattern)#
// initial upload progress
const toastId = toast.info('uploading track...');
// update progress inline
xhr.upload.addEventListener('progress', (e) => {
const percent = Math.round((e.loaded / e.total) * 100);
toast.update(toastId, `uploading track... ${percent}%`);
});
// processing updates via SSE
eventSource.onmessage = (event) => {
const update = JSON.parse(event.data);
if (update.status === 'processing') {
toast.update(toastId, update.message);
}
if (update.status === 'completed') {
toast.dismiss(toastId);
toast.success('track uploaded successfully!');
}
};
styling#
uses CSS custom properties from the global theme:
--accent: info toast icon color--success: success toast icon color--error: error toast icon color--warning: warning toast icon color--text-primary: message text- glassmorphism background with
backdrop-filter: blur(12px)
key features#
- in-place updates:
toast.update(id, newMessage)allows progress tracking without spawning multiple toasts - long-running tasks: custom durations (e.g., 30s for uploads) prevent premature dismissal
- zero dependencies: custom implementation, no external libraries
- type-safe: full TypeScript support with exported types
- consistent patterns: matches other Svelte 5 rune-based state managers
action links#
toasts can include an optional action link:
export interface ToastAction {
label: string;
href: string;
}
// usage
toast.success('track uploaded!', 3000, { label: 'view track', href: `/track/${trackId}` });
action links render as styled anchor tags. internal links stay in the same tab; external links open in new tabs.
future enhancements#
potential additions (not currently implemented):
- pause on hover to prevent auto-dismiss
- manual dismiss buttons
- progress bars for visual timing
- toast queue limiting to prevent spam