24 KiB
Implement PDF Download Modal with Interactive Thumbnails
Transform the existing PDF export modal from a placeholder "work in progress" message into a functional PDF download interface with three interactive thumbnail previews: Short CV, Long CV, and Custom (placeholder). Each thumbnail will show a stylized representation of the CV option using skeleton/placeholder styling, allowing users to visually preview and select their preferred download format.Key Goals:
- Repurpose the existing
pdf-modal.htmldialog to show three CV format options - Create thumbnail cards that represent each option visually
- Use skeleton/placeholder styling (similar to animated placeholders) for visual representation
- Make thumbnails interactive - click to select, visual highlight on selection
- Prepare foundation for PDF download functionality (actual download in future phase)
Why This Matters:
- Visual choice is easier than text: Users can see what they'll get before downloading
- Professional UX: Modern download interfaces show previews (e.g., Canva, Figma exports)
- Sets up extensibility: Foundation for future customization wizard
- Reduces user errors: Clear visual indication prevents downloading wrong format
Desired Implementation:
Three CV Format Options:
-
Short CV - One-page condensed version
- Thumbnail shows simplified, compact layout representation
- Essential info only (like current
.cv-shortclass)
-
Long CV - Full two-page detailed version
- Thumbnail shows more detailed layout representation
- All sections included (like current
.cv-longclass)
-
Custom - User-configurable version (placeholder for future)
- Thumbnail shows question mark or customization icon
- Indicates "customize your CV" option
- For now, just placeholder - implementation deferred to next phase
Thumbnail Design (Hybrid Approach):
- Not full miniature renders (too heavy), not pure skeleton boxes (too abstract)
- Stylized representations that are recognizable as CV layouts
- Use skeleton/placeholder aesthetic: rounded boxes with subtle gradients
- Short version: Single compact card showing header + 2-3 content sections
- Long version: Taller card or two cards showing header + 4-6 content sections
- Custom version: Card with large question mark or settings icon
Interaction Pattern:
- Click thumbnail card to select it (not hover-only)
- Selected card gets visual highlight (border, shadow, checkmark badge)
- Only one selection at a time (radio button behavior)
- "Download PDF" button at bottom (initially disabled until selection made)
- Button triggers download of selected format (stub for now, implement later)
Reference Files: @templates/partials/modals/pdf-modal.html - Current modal to transform @templates/partials/modals/shortcuts-modal.html - Modal pattern reference @prompts/002-animate-language-transitions.md - Skeleton loader CSS reference
Tech Stack:
- Native HTML
<dialog>element (already in use) - Hyperscript for interactions (click handlers, state management)
- CSS for thumbnail styling (skeleton gradient animations)
- Iconify icons for visual indicators
1. Modal Structure Redesign
Update PDF Modal Layout:
<dialog id="pdf-modal" class="info-modal pdf-download-modal">
<div class="info-modal-content">
<!-- Close button (keep existing pattern) -->
<button class="info-modal-close">...</button>
<!-- Header -->
<div class="info-modal-header">
<h2>Download PDF</h2>
<p class="subtitle">Choose your preferred CV format</p>
</div>
<!-- Body: Three thumbnail cards -->
<div class="pdf-options-grid">
<!-- Short CV Card -->
<div class="pdf-option-card" data-cv-format="short">
<div class="pdf-thumbnail thumbnail-short">
<!-- Stylized short CV representation -->
</div>
<div class="pdf-option-info">
<h3>Short CV</h3>
<p>One page, essential info</p>
</div>
<div class="pdf-option-badge">
<iconify-icon icon="mdi:check-circle"></iconify-icon>
</div>
</div>
<!-- Long CV Card -->
<div class="pdf-option-card" data-cv-format="long">
<div class="pdf-thumbnail thumbnail-long">
<!-- Stylized long CV representation -->
</div>
<div class="pdf-option-info">
<h3>Long CV</h3>
<p>Full version, all details</p>
</div>
<div class="pdf-option-badge">
<iconify-icon icon="mdi:check-circle"></iconify-icon>
</div>
</div>
<!-- Custom CV Card (placeholder) -->
<div class="pdf-option-card" data-cv-format="custom">
<div class="pdf-thumbnail thumbnail-custom">
<!-- Question mark or customize icon -->
</div>
<div class="pdf-option-info">
<h3>Custom</h3>
<p>Customize sections</p>
</div>
<div class="pdf-option-badge">
<iconify-icon icon="mdi:check-circle"></iconify-icon>
</div>
</div>
</div>
<!-- Footer: Download button -->
<div class="pdf-modal-footer">
<button class="pdf-download-btn" disabled>
<iconify-icon icon="mdi:download"></iconify-icon>
Download PDF
</button>
</div>
</div>
</dialog>
Grid Layout:
- Three columns on desktop (≥768px)
- Single column on mobile (<768px) with cards stacked
- Equal height cards for visual consistency
- Adequate spacing between cards (16-24px gap)
2. Thumbnail Visual Design
Skeleton/Placeholder Aesthetic:
Use the skeleton loader pattern from prompt 002 to create stylized CV representations:
/* Base thumbnail container */
.pdf-thumbnail {
background: #ffffff;
border: 1px solid #e0e0e0;
border-radius: 8px;
padding: 16px;
height: 280px; /* Adjust based on content */
display: flex;
flex-direction: column;
gap: 12px;
position: relative;
overflow: hidden;
}
/* Skeleton elements inside thumbnails */
.skeleton-block {
background: linear-gradient(90deg, #f0f0f0 25%, #e8e8e8 50%, #f0f0f0 75%);
background-size: 200% 100%;
animation: skeleton-shimmer 2s ease-in-out infinite;
border-radius: 4px;
}
@keyframes skeleton-shimmer {
0%, 100% { background-position: 200% 0; }
50% { background-position: 0 0; }
}
Short CV Thumbnail Structure:
<div class="pdf-thumbnail thumbnail-short">
<!-- Header representation -->
<div class="skeleton-block" style="height: 48px;"></div>
<!-- Content sections (compact) -->
<div class="skeleton-block" style="height: 60px;"></div>
<div class="skeleton-block" style="height: 60px;"></div>
<div class="skeleton-block" style="height: 60px;"></div>
<!-- Badge overlay -->
<div class="thumbnail-badge">1 Page</div>
</div>
Long CV Thumbnail Structure:
<div class="pdf-thumbnail thumbnail-long">
<!-- Header representation -->
<div class="skeleton-block" style="height: 48px;"></div>
<!-- More content sections (detailed) -->
<div class="skeleton-block" style="height: 40px;"></div>
<div class="skeleton-block" style="height: 40px;"></div>
<div class="skeleton-block" style="height: 40px;"></div>
<div class="skeleton-block" style="height: 40px;"></div>
<div class="skeleton-block" style="height: 40px;"></div>
<!-- Badge overlay -->
<div class="thumbnail-badge">2 Pages</div>
</div>
Custom CV Thumbnail Structure:
<div class="pdf-thumbnail thumbnail-custom">
<!-- Centered icon instead of skeleton blocks -->
<div class="custom-placeholder">
<iconify-icon icon="mdi:help-circle-outline" width="80" height="80"></iconify-icon>
<p>Customize</p>
</div>
<!-- Badge overlay -->
<div class="thumbnail-badge">Coming Soon</div>
</div>
Visual Distinctions:
- Short thumbnail: Fewer, larger blocks (compact feel)
- Long thumbnail: More, smaller blocks (detailed feel)
- Custom thumbnail: No skeleton blocks, just icon + text
- All thumbnails: Page count or status badge in corner
3. Interactive Selection Behavior
Click to Select Pattern:
Use hyperscript to manage selection state:
-- On each PDF option card
on click
-- Remove selected class from all cards
set cards to .pdf-option-card in #pdf-modal
for card in cards
remove .selected from card
end
-- Add selected class to clicked card
add .selected to me
-- Enable download button
set downloadBtn to .pdf-download-btn in #pdf-modal
remove @disabled from downloadBtn
-- Store selected format for later
set :selectedFormat to my @data-cv-format
end
CSS Selection States:
/* Default card state */
.pdf-option-card {
border: 2px solid transparent;
border-radius: 12px;
padding: 16px;
cursor: pointer;
transition: all 250ms ease;
position: relative;
background: #ffffff;
}
/* Hover state */
.pdf-option-card:hover {
border-color: #e0e0e0;
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1);
transform: translateY(-2px);
}
/* Selected state */
.pdf-option-card.selected {
border-color: #4caf50; /* or brand color */
box-shadow: 0 6px 16px rgba(76, 175, 80, 0.2);
background: #f9fff9; /* subtle tint */
}
/* Selected badge (checkmark) */
.pdf-option-badge {
position: absolute;
top: 8px;
right: 8px;
opacity: 0;
transform: scale(0.8);
transition: all 250ms ease;
color: #4caf50;
}
.pdf-option-card.selected .pdf-option-badge {
opacity: 1;
transform: scale(1);
}
Download Button State:
/* Disabled state (default) */
.pdf-download-btn:disabled {
background: #e0e0e0;
color: #999999;
cursor: not-allowed;
opacity: 0.6;
}
/* Enabled state (after selection) */
.pdf-download-btn:not(:disabled) {
background: #4caf50;
color: white;
cursor: pointer;
}
.pdf-download-btn:not(:disabled):hover {
background: #45a049;
box-shadow: 0 4px 12px rgba(76, 175, 80, 0.3);
}
4. Multilingual Support
Update Template with Language Conditionals:
<h2>{{if eq .Lang "es"}}Descargar PDF{{else}}Download PDF{{end}}</h2>
<p class="subtitle">
{{if eq .Lang "es"}}Elige tu formato preferido{{else}}Choose your preferred format{{end}}
</p>
<!-- Short CV Card -->
<h3>{{if eq .Lang "es"}}CV Corto{{else}}Short CV{{end}}</h3>
<p>{{if eq .Lang "es"}}Una página, información esencial{{else}}One page, essential info{{end}}</p>
<!-- Long CV Card -->
<h3>{{if eq .Lang "es"}}CV Completo{{else}}Long CV{{end}}</h3>
<p>{{if eq .Lang "es"}}Versión completa, todos los detalles{{else}}Full version, all details{{end}}</p>
<!-- Custom CV Card -->
<h3>{{if eq .Lang "es"}}Personalizado{{else}}Custom{{end}}</h3>
<p>{{if eq .Lang "es"}}Personaliza secciones{{else}}Customize sections{{end}}</p>
<!-- Download Button -->
<button class="pdf-download-btn" disabled>
<iconify-icon icon="mdi:download"></iconify-icon>
{{if eq .Lang "es"}}Descargar PDF{{else}}Download PDF{{end}}
</button>
5. Responsive Design
Mobile Optimizations:
/* Desktop: Three columns */
@media (min-width: 768px) {
.pdf-options-grid {
display: grid;
grid-template-columns: repeat(3, 1fr);
gap: 24px;
}
}
/* Tablet: Two columns */
@media (min-width: 480px) and (max-width: 767px) {
.pdf-options-grid {
display: grid;
grid-template-columns: repeat(2, 1fr);
gap: 16px;
}
/* Custom card spans full width */
.pdf-option-card[data-cv-format="custom"] {
grid-column: 1 / -1;
}
}
/* Mobile: Single column */
@media (max-width: 479px) {
.pdf-options-grid {
display: flex;
flex-direction: column;
gap: 16px;
}
.pdf-thumbnail {
height: 200px; /* Shorter on mobile */
}
}
6. Accessibility
ARIA Attributes:
<div class="pdf-option-card"
data-cv-format="short"
role="radio"
aria-checked="false"
aria-label="Short CV - One page, essential information"
tabindex="0">
...
</div>
Keyboard Navigation:
-- On PDF option cards
on keydown
if event.key is 'Enter' or event.key is ' '
halt the event
trigger click on me
end
end
Screen Reader Announcements:
<div class="sr-only" aria-live="polite" id="selection-announcement"></div>
<!-- Update on selection -->
<script>
document.getElementById('selection-announcement').textContent =
'Selected: Short CV - One page format';
</script>
7. PDF Download Stub (Future Implementation)
For Now:
- Download button triggers a function that logs selected format
- Shows a toast/alert: "PDF download coming soon!"
- Stores selection in variable for when backend is ready
Hyperscript Stub:
-- On download button
on click
if :selectedFormat is not null
log 'Download requested for format:', :selectedFormat
-- TODO: Trigger actual PDF download when backend ready
call alert('PDF download coming soon! Selected format: ' + :selectedFormat)
end
end
Preparation for Real Implementation:
- Format selection stored in
:selectedFormatvariable - Can easily be sent to backend:
hx-get="/download-pdf?format={format}" - Modal can stay open or close after download
- Consider adding loading spinner during download
Step-by-Step Implementation Plan
Phase 1: Update PDF Modal Structure
-
Modify
templates/partials/modals/pdf-modal.html:- Remove "work in progress" placeholder content
- Add new modal structure with header, options grid, footer
- Include multilingual text with
{{if eq .Lang}}conditionals - Add close button (keep existing pattern)
-
Create Thumbnail HTML Structures:
- Short CV thumbnail with 3-4 skeleton blocks
- Long CV thumbnail with 5-6 skeleton blocks
- Custom CV thumbnail with centered icon
- Add page count badges to each thumbnail
Phase 2: Create CSS Styles
-
Add Modal Layout Styles to
static/css/main.css:.pdf-options-grid- responsive grid layout.pdf-option-card- card container styles- Card states: default, hover, selected
.pdf-option-badge- checkmark badge positioning
-
Add Thumbnail Styles:
.pdf-thumbnail- base thumbnail container.skeleton-block- reuse skeleton loader pattern from prompt 002@keyframes skeleton-shimmer- shimmer animation.thumbnail-badge- page count badge overlay- Responsive thumbnail heights
-
Add Footer/Button Styles:
.pdf-modal-footer- footer layout.pdf-download-btn- button base styles- Button states: disabled, enabled, hover
- Icon + text layout
Phase 3: Implement Selection Logic
-
Add Hyperscript to Cards:
- Click handler to select card
- Remove selection from other cards
- Add
.selectedclass to clicked card - Store selected format in variable
- Enable download button
-
Add Keyboard Support:
- Tab navigation between cards
- Enter/Space to select card
- ESC to close modal (already handled by dialog)
-
Add Selection State Management:
- ARIA attributes for screen readers
- Visual feedback (border, shadow, badge)
- Announcement for screen readers
Phase 4: Implement Download Button (Stub)
-
Add Click Handler to Download Button:
- Check if format is selected
- Log selected format (for debugging)
- Show alert/toast: "Coming soon!"
- Prepare structure for real implementation
-
Prepare for Backend Integration:
- Document expected API endpoint:
/download-pdf?format={format} - Document expected response: PDF file download
- Add TODO comments for future implementation
- Document expected API endpoint:
Phase 5: Testing and Refinement
-
Visual Testing:
- Three thumbnails display correctly
- Skeleton shimmer animations are smooth
- Cards respond to hover and selection
- Badge appears on selection
- Download button enables/disables correctly
-
Interaction Testing:
- Click to select works on all three cards
- Only one card selected at a time
- Download button requires selection
- Keyboard navigation works (tab, enter, space)
-
Responsive Testing:
- Desktop: Three columns side-by-side
- Tablet: Two columns with custom spanning
- Mobile: Single column stacked
- Thumbnails adapt height appropriately
-
Multilingual Testing:
- Switch to Spanish: All text translates
- Switch to English: All text translates
- No hardcoded text in templates
What to Prioritize:
- Core modal structure and thumbnail layout
- Selection interaction (click to highlight)
- Visual polish (skeleton shimmer, badges)
- Responsive design
- Accessibility features
What to Avoid:
- Don't implement actual PDF generation yet (out of scope)
- Don't make thumbnails too complex (keep stylized and simple)
- Don't use images for thumbnails (pure CSS/HTML)
- Don't forget mobile UX (touch targets, stacked layout)
- Don't hardcode text (use multilingual template conditionals)
Why These Constraints Matter:
- Stylized over realistic: Faster to render, easier to maintain, loads instantly
- Skeleton aesthetic: Consistent with existing placeholder patterns, modern UX
- Interactive selection: Better UX than radio buttons, visual feedback is immediate
- Stub download: Allows testing full flow without backend dependency
- Accessibility: Ensures all users can navigate and select options
-
./templates/partials/modals/pdf-modal.html- Transform from placeholder to functional modal
- Add three thumbnail cards (short, long, custom)
- Add selection interaction with hyperscript
- Add download button (stub functionality)
- Include multilingual support
-
./static/css/main.css(add PDF modal styles section)- Modal layout and grid styles
- Card styles (default, hover, selected states)
- Thumbnail container and skeleton block styles
- Skeleton shimmer animation keyframes
- Badge and button styles
- Responsive breakpoints
Optional:
3. ./static/css/pdf-modal.css (NEW - if you prefer separate file)
- Dedicated stylesheet for PDF modal component
- Import in main CSS or link in template
- Add UI text to
data/ui-en.jsonanddata/ui-es.json(if using centralized UI strings)- PDF modal title and subtitle
- Option names and descriptions
- Button text
1. Visual Verification:
- Modal opens with three thumbnail cards displayed
- Short CV thumbnail shows 3-4 skeleton blocks (compact)
- Long CV thumbnail shows 5-6 skeleton blocks (detailed)
- Custom CV thumbnail shows question mark/icon
- Skeleton blocks have subtle shimmer animation
- Page count badges visible on thumbnails ("1 Page", "2 Pages", "Coming Soon")
- Download button initially disabled (grayed out)
2. Selection Interaction:
- Click Short CV card: Border highlights, checkmark badge appears
- Click Long CV card: Previous selection clears, new card highlights
- Click Custom CV card: Selection transfers correctly
- Only one card selected at any time (radio button behavior)
- Download button enables after selection
- Download button disables if no selection (edge case testing)
3. Download Button (Stub):
- Button disabled when modal first opens
- Button enables after selecting any option
- Click button: Shows "Coming soon" alert (or console log)
- Selected format is stored and retrievable
- Button styling changes between disabled/enabled states
4. Responsive Design:
- Desktop (≥768px): Three columns, equal width
- Tablet (480-767px): Two columns, custom card spans full width
- Mobile (<480px): Single column, cards stacked vertically
- Thumbnails resize appropriately on different screens
- Modal remains centered and scrollable on small screens
5. Multilingual Support:
- English: All text in English
- Spanish: All text in Spanish
- Toggle language: Modal text updates correctly
- No untranslated or hardcoded English text
6. Accessibility:
- Tab navigation: Can tab through all three cards
- Enter key: Selects focused card
- Space key: Selects focused card
- Screen reader: Announces card selection
- ARIA attributes: role="radio", aria-checked updates
- Focus indicators visible on cards
- Download button keyboard accessible
7. Animation Performance:
- Skeleton shimmer is smooth (60fps, no jank)
- Selection transition is smooth (border, shadow appear smoothly)
- Hover effects are responsive (no delay or lag)
- Animations respect
prefers-reduced-motion
8. Modal Behavior:
- Modal opens when triggered (test trigger button)
- Close button (X) closes modal
- Click outside modal closes it (backdrop click)
- ESC key closes modal
- Selection state persists while modal is open
- Selection resets when modal reopens (or persists - decide which)
9. Edge Cases:
- Rapidly click different cards - no broken states
- Close modal and reopen - state resets correctly
- Switch language while modal open - text updates
- Very long text doesn't break layout (test with Spanish)
Success Indicators: ✅ PDF modal transformed from placeholder to functional interface ✅ Three thumbnail cards with stylized CV representations ✅ Skeleton shimmer animations working smoothly ✅ Click-to-select interaction with visual feedback ✅ Download button enables/disables based on selection ✅ Responsive layout adapts to mobile/tablet/desktop ✅ Multilingual support for English and Spanish ✅ Keyboard navigation and screen reader support ✅ Professional, polished visual design ✅ Foundation prepared for future PDF download implementation
<success_criteria>
- PDF modal displays three interactive thumbnail options
- Thumbnails use skeleton/placeholder styling with shimmer animation
- Short CV thumbnail shows compact layout (fewer blocks)
- Long CV thumbnail shows detailed layout (more blocks)
- Custom CV thumbnail shows placeholder icon/message
- Click-to-select interaction highlights chosen option
- Visual selection feedback: border, shadow, checkmark badge
- Download button enabled only when option selected
- Responsive grid layout: 3 columns desktop, 2 tablet, 1 mobile
- Multilingual support in English and Spanish
- Keyboard accessible with proper ARIA attributes
- Smooth animations (60fps shimmer, 250ms transitions)
- Modal follows existing project patterns (dialog element, hyperscript)
- Code is maintainable and well-documented
- Foundation ready for backend PDF generation integration </success_criteria>
Questions to Answer:
- What skeleton/placeholder styles already exist?
- How are other modals styled (pattern consistency)?
- What multilingual pattern is used for UI text?
- What iconify icons are available for badges/buttons?
- What hyperscript patterns are used elsewhere?
<additional_notes> Design Philosophy:
- Stylized over realistic: Faster, lighter, easier to maintain
- Progressive disclosure: Show options first, download later
- Visual feedback first: Don't make users guess what they selected
- Mobile-friendly: Touch targets, stacked layout, clear labels
Future Extension Points:
- Custom option will eventually open customization wizard
- Download button will trigger server-side PDF generation
- Could add "Preview" button to show full-size before download
- Could add "Email PDF" option alongside download
- Could remember last selection in localStorage
Technical Decisions:
- Use skeleton blocks instead of miniature renders (performance)
- Use CSS animations instead of JavaScript (smooth, performant)
- Use native dialog element (accessibility, browser features)
- Use hyperscript for state management (consistent with project)
Visual Design Notes:
- Skeleton shimmer should be subtle (not distracting)
- Selected state should be obvious (green border + checkmark)
- Thumbnails should be recognizable as CV layouts
- Cards should feel clickable (cursor, hover effects)
- Download button should be prominent when enabled </additional_notes>