audio streaming app
plyr.fm
1# toast notification system
2
3## status
4
5**IMPLEMENTED** - this feature is live in production
6
7## overview
8
9lightweight, zero-dependency toast notification system providing immediate user feedback for async operations (file uploads, network errors, etc.).
10
11## use cases
12
13the toast system provides UX feedback for:
14- **uploads**: "uploading track... 45%" → "track uploaded successfully!"
15- **upload errors**: detailed error messages with specific failure reasons
16- **network errors**: "network error: connection failed"
17- **processing updates**: real-time SSE progress updates during transcoding
18- **general notifications**: success/error/info/warning messages throughout the app
19
20## implementation
21
22### state management
23
24uses svelte 5 class with `$state` runes (consistent with `player.svelte.ts` and other state managers):
25
26```typescript
27// frontend/src/lib/toast.svelte.ts
28class ToastState {
29 toasts = $state<Toast[]>([]);
30
31 add(message: string, type: ToastType = 'info', duration = 3000): string
32 dismiss(id: string): void
33 update(id: string, message: string, type?: ToastType): void
34 success(message: string, duration = 3000): string
35 error(message: string, duration = 5000): string
36 info(message: string, duration = 3000): string
37 warning(message: string, duration = 4000): string
38}
39export const toast = new ToastState();
40```
41
42### toast types and timing
43
44- **success**: 3s auto-dismiss, ✓ icon, green accent
45- **error**: 5s auto-dismiss, ✕ icon, red accent
46- **info**: 3s auto-dismiss, ℹ icon, blue accent
47- **warning**: 4s auto-dismiss, ⚠ icon, orange accent
48- custom duration supported: `toast.add('message', 'info', 10000)`
49
50### visual design
51
52- dark background with glassmorphism (backdrop-filter blur)
53- type-specific icon colors using CSS variables
54- positioned bottom-left (above player on mobile)
55- fade transitions (respects `prefers-reduced-motion`)
56
57### positioning
58
59**all devices**: bottom-left corner
60- positioned above player footer using `calc(var(--player-height) + 1rem)`
61- doesn't block main content
62- stacks vertically with newest on top
63- responsive padding adjustments for mobile
64
65### accessibility
66
67implemented features:
68- `role="region"` + `aria-live="polite"` on container
69- `role="alert"` on individual toasts
70- `aria-hidden="true"` on decorative icons
71- respects `prefers-reduced-motion` media query
72- auto-dismiss ensures toasts don't linger indefinitely
73
74## usage patterns
75
76### simple notifications
77```typescript
78import { toast } from '$lib/toast.svelte';
79
80toast.success('track uploaded');
81toast.error('upload failed');
82toast.info('processing...');
83toast.warning('approaching rate limit');
84```
85
86### progress updates (uploader pattern)
87```typescript
88// initial upload progress
89const toastId = toast.info('uploading track...');
90
91// update progress inline
92xhr.upload.addEventListener('progress', (e) => {
93 const percent = Math.round((e.loaded / e.total) * 100);
94 toast.update(toastId, `uploading track... ${percent}%`);
95});
96
97// processing updates via SSE
98eventSource.onmessage = (event) => {
99 const update = JSON.parse(event.data);
100 if (update.status === 'processing') {
101 toast.update(toastId, update.message);
102 }
103 if (update.status === 'completed') {
104 toast.dismiss(toastId);
105 toast.success('track uploaded successfully!');
106 }
107};
108```
109
110## styling
111
112uses CSS custom properties from the global theme:
113- `--accent`: info toast icon color
114- `--success`: success toast icon color
115- `--error`: error toast icon color
116- `--warning`: warning toast icon color
117- `--text-primary`: message text
118- glassmorphism background with `backdrop-filter: blur(12px)`
119
120## key features
121
122- **in-place updates**: `toast.update(id, newMessage)` allows progress tracking without spawning multiple toasts
123- **long-running tasks**: custom durations (e.g., 30s for uploads) prevent premature dismissal
124- **zero dependencies**: custom implementation, no external libraries
125- **type-safe**: full TypeScript support with exported types
126- **consistent patterns**: matches other Svelte 5 rune-based state managers
127
128### action links
129
130toasts can include an optional action link:
131
132```typescript
133export interface ToastAction {
134 label: string;
135 href: string;
136}
137
138// usage
139toast.success('track uploaded!', 3000, { label: 'view track', href: `/track/${trackId}` });
140```
141
142action links render as styled anchor tags. internal links stay in the same tab; external links open in new tabs.
143
144## future enhancements
145
146potential additions (not currently implemented):
147- pause on hover to prevent auto-dismiss
148- manual dismiss buttons
149- progress bars for visual timing
150- toast queue limiting to prevent spam