# Toast Notification System **Comprehensive documentation for the CV project's toast notification system.** --- ## Table of Contents 1. [Overview](#overview) 2. [Architecture](#architecture) 3. [User Experience Flows](#user-experience-flows) 4. [Implementation Details](#implementation-details) 5. [JavaScript API](#javascript-api) 6. [CSS Structure](#css-structure) 7. [Template Structure](#template-structure) 8. [Integration with PDF Downloads](#integration-with-pdf-downloads) 9. [Accessibility](#accessibility) 10. [Testing](#testing) 11. [Customization Guide](#customization-guide) 12. [Troubleshooting](#troubleshooting) --- ## Overview The toast notification system provides non-intrusive visual feedback for asynchronous operations, particularly PDF generation and downloads. It enhances user experience by: - **Immediate Feedback**: Users know their action was registered - **Progress Tracking**: Visual indication of operation duration - **Completion Confirmation**: Clear success/error messaging - **Non-Blocking UX**: Users can continue working while operations complete ### Key Features ✅ Multiple toast variants (success, error, info, warning) ✅ Animated slide-in/slide-out ✅ Progress bar with timing animation ✅ Auto-dismiss with configurable duration ✅ Manual close button ✅ Mobile responsive (bottom-center on small screens) ✅ Accessibility compliant (ARIA labels, reduced motion support) ✅ Bilingual support (English/Spanish) --- ## Architecture ### Component Structure ``` Toast System │ ├── CSS Layer (static/css/04-interactive/_toasts.css) │ ├── Toast container positioning │ ├── Variant styles (success, error, info, warning) │ ├── Animations (slide, fade, progress) │ └── Responsive breakpoints │ ├── HTML Templates (templates/partials/widgets/) │ ├── error-toast.html (Error notifications) │ └── pdf-toast.html (PDF download feedback) │ ├── JavaScript API (static/js/main.js) │ ├── showPDFToast(options) │ ├── hidePDFToast() │ └── showError(message) │ └── Integration Layer └── PDF Modal (templates/partials/modals/pdf-modal.html) └── downloadPDF() function ``` ### Design Principles 1. **Progressive Enhancement**: Works without JavaScript (fallback to modal only) 2. **Separation of Concerns**: CSS handles animations, JS handles logic 3. **Reusability**: Generic toast system extensible for any notification type 4. **Performance**: Minimal DOM manipulation, CSS-driven animations 5. **Accessibility**: Screen reader announcements, keyboard navigation --- ## User Experience Flows ### Flow 1: PDF Download (Modal Stays Open) ``` User clicks "Download PDF" ↓ Modal shows loading overlay "⏳ Preparing PDF... (~4 seconds)" ↓ User waits in modal ↓ PDF downloads to browser ↓ Modal auto-closes ↓ DONE ✅ ``` **When to use:** Default behavior for users who want to watch progress. ### Flow 2: PDF Download (User Closes Modal) ``` User clicks "Download PDF" ↓ Modal shows loading overlay ↓ User presses ESC or clicks X ↓ Toast slides in (bottom-right) "📥 Preparing PDF..." "Generating Default CV (5 pages)... (~4s)" [Progress bar animates] ↓ After estimated time (~4s) ↓ Toast updates "✅ PDF Ready!" "Check your downloads folder" ↓ Toast auto-dismisses (3s) ↓ DONE ✅ ``` **When to use:** Power users who want to continue working while PDF generates. --- ## Implementation Details ### File Structure ``` cv/ ├── static/css/04-interactive/ │ └── _toasts.css # Toast styles (242 lines) ├── templates/partials/widgets/ │ ├── error-toast.html # Error notification template │ └── pdf-toast.html # PDF download notification template ├── templates/index.html # Includes toast templates ├── static/js/main.js # Toast functions └── templates/partials/modals/ └── pdf-modal.html # Integrates toast with downloads ``` ### CSS Organization **File:** `static/css/04-interactive/_toasts.css` **Sections:** 1. **Base Toast Container** - Positioning, layout, common styles 2. **Toast Variants** - Color schemes for different notification types 3. **Content Elements** - Icon, title, message, close button 4. **Progress Bar** - Visual countdown indicator 5. **Animations** - Slide-in, slide-out, lifecycle, progress 6. **Responsive Design** - Mobile breakpoints 7. **Accessibility** - Reduced motion preferences 8. **Print Styles** - Hide toasts in print media --- ## JavaScript API ### `showPDFToast(options)` Display a PDF download toast notification. **Parameters:** ```javascript { icon: string, // Emoji icon (📥, ✅, ⚠️) title: string, // Toast title message: string, // Toast message duration: number, // Auto-hide duration in ms (default: 5000) autoHide: boolean // Auto-dismiss after duration (default: true) } ``` **Example:** ```javascript window.showPDFToast({ icon: '📥', title: 'Preparing PDF...', message: 'Generating Default CV (5 pages)... (~4s)', duration: 4000, autoHide: false // Manually control dismissal }); ``` **Return:** None **Side Effects:** - Adds `.show` class to toast element - Starts progress bar animation - Schedules auto-hide timeout (if `autoHide: true`) --- ### `hidePDFToast()` Immediately hide the PDF toast. **Parameters:** None **Example:** ```javascript window.hidePDFToast(); ``` **Return:** None **Side Effects:** - Removes `.show` class from toast element - Cancels progress bar animation --- ### `showError(message)` Display an error toast notification. **Parameters:** ```javascript message: string // Error message to display ``` **Example:** ```javascript window.showError('Failed to load content. Please try again.'); ``` **Return:** None **Side Effects:** - Shows error toast with red styling - Auto-hides after 5 seconds (CSS animation) --- ## CSS Structure ### Toast Variants #### Success Toast (Green) ```css .success-toast { background: linear-gradient(135deg, rgba(40, 167, 69, 0.95) 0%, rgba(25, 135, 84, 0.95) 100%); color: white; border-left: 4px solid #fff; } ``` #### Error Toast (Red) ```css .error-toast { background: linear-gradient(135deg, rgba(220, 53, 69, 0.95) 0%, rgba(200, 35, 51, 0.95) 100%); color: white; border-left: 4px solid #fff; } ``` #### Info Toast (Blue) ```css .info-toast { background: linear-gradient(135deg, rgba(13, 110, 253, 0.95) 0%, rgba(10, 88, 202, 0.95) 100%); color: white; border-left: 4px solid #fff; } ``` #### Warning Toast (Yellow) ```css .warning-toast { background: linear-gradient(135deg, rgba(255, 193, 7, 0.95) 0%, rgba(255, 167, 38, 0.95) 100%); color: #333; border-left: 4px solid #333; } ``` ### Key Animations #### Slide In ```css @keyframes toastSlideIn { from { opacity: 0; transform: translateX(100%) translateY(0); } to { opacity: 1; transform: translateX(0) translateY(0); } } ``` #### Lifecycle (Auto-hide) ```css @keyframes toastLifecycle { 0% { opacity: 1; transform: translateX(0) translateY(0); } 85% { opacity: 1; transform: translateX(0) translateY(0); } 100% { opacity: 0; transform: translateX(100%) translateY(0); } } ``` #### Progress Bar ```css @keyframes progressShrink { from { width: 100%; } to { width: 0%; } } ``` --- ## Template Structure ### PDF Toast Template **File:** `templates/partials/widgets/pdf-toast.html` ```html
📥

Preparing PDF

``` ### Key Elements - **`role="status"`**: ARIA role for status messages - **`aria-live="polite"`**: Screen reader announcement (non-interrupting) - **`aria-atomic="true"`**: Read entire toast as single unit - **`.no-print`**: Hidden in print media - **`#pdf-toast-icon`**: Dynamically updated emoji - **`#pdf-toast-title`**: Main heading - **`#pdf-toast-message`**: Detailed message - **`.toast-progress-bar`**: Visual countdown indicator --- ## Integration with PDF Downloads ### Download Flow Integration **File:** `templates/partials/modals/pdf-modal.html` ```javascript function downloadPDF() { // ... setup code ... // Track if modal is closed by user let modalClosedByUser = false; const onModalClose = () => { modalClosedByUser = true; // Show toast when user closes modal if (window.showPDFToast) { window.showPDFToast({ icon: '📥', title: isSpanish ? 'Preparando PDF...' : 'Preparing PDF...', message: `Generating ${formatName}... (~${estimatedTime}s)`, duration: estimatedTime * 1000, autoHide: false }); } }; // Listen for modal close modal.addEventListener('close', onModalClose, { once: true }); // Trigger download window.location.href = url; // After estimated time: update toast or close modal setTimeout(() => { if (modalClosedByUser && window.showPDFToast) { // Update toast to success window.showPDFToast({ icon: '✅', title: isSpanish ? '¡PDF Listo!' : 'PDF Ready!', message: isSpanish ? 'Revisa tu carpeta de descargas' : 'Check your downloads folder', duration: 3000, autoHide: true }); } else { // Close modal if still open modal.close(); } }, estimatedTime * 1000); } ``` ### Format-Specific Timing | PDF Format | Pages | Estimated Time | Icon Progression | |------------|-------|----------------|------------------| | Short | 4 | 3 seconds | 📥 → ✅ | | Default | 5 | 4 seconds | 📥 → ✅ | | Extended | 9 | 8 seconds | 📥 → ✅ | --- ## Accessibility ### ARIA Attributes ```html
aria-live="polite" aria-atomic="true"> ``` ### Screen Reader Behavior 1. **Toast Appears**: Screen reader announces title + message 2. **Toast Updates**: New content announced automatically 3. **Toast Dismisses**: Silent (user already informed) ### Keyboard Navigation - **Close Button**: Focusable with `Tab` - **Enter/Space**: Activate close button - **ESC**: Closes parent modal (which may trigger toast) ### Reduced Motion Users with motion sensitivity get simplified animations: ```css @media (prefers-reduced-motion: reduce) { .success-toast, .error-toast, .toast { animation: none; opacity: 1; } .toast-progress-bar { animation: none; } } ``` **Effect:** Toast appears/disappears instantly without sliding/fading. --- ## Testing ### Automated Test **File:** `tests/mjs/29-pdf-toast-notifications.test.mjs` **Coverage:** - ✅ Toast elements exist in DOM - ✅ Toast initially hidden - ✅ JavaScript functions available - ✅ Manual toast trigger works - ✅ Toast auto-hides after duration - ✅ PDF modal integration (Scenario A: Stay in modal) - ✅ PDF modal integration (Scenario B: Close modal early) - ✅ CSS animations defined - ✅ Icon/title/message updates - ✅ Progress bar animation **Run Test:** ```bash bun tests/mjs/29-pdf-toast-notifications.test.mjs ``` ### Manual Testing Checklist **Scenario 1: Modal Overlay Only** - [ ] Open PDF modal - [ ] Select Default CV - [ ] Click Download PDF - [ ] Wait in modal without closing - [ ] Verify overlay shows with spinner - [ ] Verify modal closes after ~4 seconds - [ ] Verify NO toast appears **Scenario 2: Toast After Modal Close** - [ ] Open PDF modal - [ ] Select Extended CV (9 pages) - [ ] Click Download PDF - [ ] Immediately press ESC to close modal - [ ] Verify toast appears bottom-right - [ ] Verify icon is 📥 - [ ] Verify title says "Preparing PDF..." - [ ] Verify message includes format name and time - [ ] Verify progress bar animates (shrinks) - [ ] Wait ~8 seconds - [ ] Verify toast updates to ✅ - [ ] Verify title says "PDF Ready!" - [ ] Verify toast auto-dismisses after ~3 seconds **Scenario 3: Close Button** - [ ] Trigger toast manually (browser console): `showPDFToast({icon: '🧪', title: 'Test', message: 'Test message'})` - [ ] Click × close button - [ ] Verify toast disappears immediately **Scenario 4: Mobile Responsive** - [ ] Resize browser to < 540px width - [ ] Trigger toast - [ ] Verify toast is full-width at bottom - [ ] Verify content is readable - [ ] Verify close button accessible --- ## Customization Guide ### Adding a New Toast Variant **1. Define CSS Class** (`static/css/04-interactive/_toasts.css`): ```css .custom-toast { background: linear-gradient(135deg, rgba(156, 39, 176, 0.95) 0%, rgba(123, 31, 162, 0.95) 100%); color: white; border-left: 4px solid #fff; } ``` **2. Create HTML Template** (`templates/partials/widgets/custom-toast.html`): ```html {{define "custom-toast"}}
🎨

Custom Title

{{end}} ``` **3. Add JavaScript Function** (`static/js/main.js`): ```javascript window.showCustomToast = function(options = {}) { const toast = document.getElementById('custom-toast'); const icon = document.getElementById('custom-toast-icon'); const title = document.getElementById('custom-toast-title'); const message = document.getElementById('custom-toast-message'); if (options.icon) icon.textContent = options.icon; if (options.title) title.textContent = options.title; if (options.message) message.textContent = options.message; toast.classList.remove('show'); void toast.offsetWidth; toast.classList.add('show'); if (options.autoHide !== false) { const duration = options.duration || 5000; setTimeout(() => toast.classList.remove('show'), duration); } }; ``` **4. Include in Index** (`templates/index.html`): ```html {{template "custom-toast" .}} ``` ### Changing Animation Duration **CSS** (`_toasts.css`): ```css /* Change lifecycle duration (default: 5s) */ .toast.show { animation: toastLifecycle 8s ease forwards; /* Now 8 seconds */ } /* Change progress bar duration */ .toast-progress-bar { animation: progressShrink 8s linear forwards; /* Match lifecycle */ } ``` **JavaScript** (when calling function): ```javascript window.showPDFToast({ icon: '📥', title: 'Preparing...', message: 'This will take longer', duration: 10000 // 10 seconds }); ``` --- ## Troubleshooting ### Toast Not Appearing **Symptom:** Calling `showPDFToast()` has no effect. **Checks:** 1. Verify toast element exists: `document.getElementById('pdf-toast')` 2. Check console for JavaScript errors 3. Verify CSS is loaded: Inspect element and check for `.success-toast` styles 4. Confirm `toasts.css` imported in `main.css` 5. Check `index.html` includes `{{template "pdf-toast" .}}` **Solution:** ```bash # Verify CSS import grep "toasts.css" static/css/main.css # Verify template inclusion grep "pdf-toast" templates/index.html # Check browser console # Should see no errors when calling showPDFToast() ``` --- ### Toast Not Auto-Dismissing **Symptom:** Toast appears but never disappears. **Checks:** 1. Verify `autoHide` is not set to `false` 2. Check CSS animation: `toastLifecycle` should be defined 3. Inspect element: `animation` property should be set 4. Check for `prefers-reduced-motion` (disables animations) **Solution:** ```javascript // Explicitly set autoHide window.showPDFToast({ icon: '✅', title: 'Test', message: 'Should auto-hide', duration: 3000, autoHide: true // Explicitly enable }); // Or manually hide setTimeout(() => window.hidePDFToast(), 3000); ``` --- ### Progress Bar Not Animating **Symptom:** Progress bar appears but doesn't shrink. **Checks:** 1. Verify `progressShrink` animation is defined in CSS 2. Check animation is applied: Inspect `#pdf-toast-progress` 3. Confirm `prefers-reduced-motion` is not active 4. Check `duration` parameter matches animation duration **Solution:** ```javascript // Ensure progress bar animation resets const progressBar = document.getElementById('pdf-toast-progress'); if (progressBar) { progressBar.style.animation = 'none'; void progressBar.offsetWidth; // Trigger reflow progressBar.style.animation = 'progressShrink 5000ms linear forwards'; } ``` --- ### Toast Overlaps Content on Mobile **Symptom:** Toast covers important content on small screens. **Checks:** 1. Verify responsive CSS is loaded 2. Check viewport width: `< 540px` should trigger mobile styles 3. Inspect `z-index` conflicts **Solution:** ```css /* Adjust mobile positioning */ @media (max-width: 540px) { .toast { bottom: 1rem; /* Lower if needed */ right: 1rem; left: 1rem; z-index: 10000; /* Ensure on top */ } } ``` --- ### Multiple Toasts Stacking **Symptom:** Calling `showPDFToast()` multiple times creates overlapping toasts. **Root Cause:** Current system uses single toast element, so multiple calls update the same toast. **Expected Behavior:** Later calls replace earlier toast content. **If You Need Multiple Toasts:** 1. Clone toast element before showing: ```javascript function showMultipleToasts(options) { const original = document.getElementById('pdf-toast'); const clone = original.cloneNode(true); clone.id = `toast-${Date.now()}`; document.body.appendChild(clone); // Update clone content and show // ... similar logic to showPDFToast ... } ``` 2. Or use a toast queue system (advanced). --- ## Related Documentation - [2. Modern Web Techniques](2-MODERN-WEB-TECHNIQUES.md) - HTMX patterns and hypermedia architecture - [12. CSS Architecture](12-CSS-ARCHITECTURE.md) - Modular CSS structure and ITCSS organization - [3. API Reference](3-API.md) - Backend API endpoints including PDF export - [11. PDF Export](11-PDF-EXPORT.md) - PDF generation with chromedp --- **Last Updated**: 2025-11-20 **Version**: 1.0 **Status**: Production Ready **Test Coverage**: 29-pdf-toast-notifications.test.mjs (22 assertions)