Commit Graph

61 Commits

Author SHA1 Message Date
juanatsap a97d6bc3fd chore: remove unused skeleton-loader.html template
Skeleton loading functionality is already implemented inline in each
section template (header.html, etc.) with .actual-content and
.skeleton-content divs. The CSS in _skeleton.css handles the loading
state, and JS in main.js toggles the .loading class.

This standalone file was never rendered by any Go handler and served
no purpose. All skeleton tests pass after removal (7/7).
2025-12-01 14:08:30 +00:00
juanatsap 9a848e8c53 feat: Add CMD+K command palette with ninja-keys integration
Implement a command palette accessible via CMD+K/Ctrl+K using the ninja-keys
web component. Features include:

- New /api/cmd-k endpoint serving dynamic CV entries (experiences, projects, courses)
- Language-aware responses with 1-hour cache headers
- Scroll-to-section functionality for quick navigation
- Enhanced keyboard shortcuts modal with CMD+K documentation
- Comprehensive test coverage for API and UI interactions

Also includes cleanup of deprecated debug test files and various UI polish
improvements to contact form, themes, and action bar components.
2025-12-01 13:03:06 +00:00
juanatsap 976b8ae2e2 fix: Scale floating button icons proportionally on mobile viewports
Remove hardcoded width/height HTML attributes from iconify-icon elements
that were overriding CSS sizing. The iconify-icon component uses HTML
attributes for SVG rendering, ignoring CSS width/height.

- Remove width="28" height="28" from 8 button templates
- Remove conflicting 768px media query from _buttons.css
- Add default desktop icon sizes (24px) in _scroll-behavior.css
- Icons now scale via clamp() from 18px (380px) to 24px (900px)
2025-12-01 12:31:31 +00:00
juanatsap 58c1237326 feat: Add secure contact form with comprehensive security features
- Add contact form dialog with HTMX integration (hx-post)
- Implement browser-only access middleware (blocks curl/Postman/wget)
- Add rate limiting (5 requests/hour per IP) for contact endpoint
- Implement honeypot and timing-based bot detection
- Add input validation (email format, message length 10-5000 chars)
- Create contact button in desktop and mobile navigation (last position)

Security features:
- Browser-only middleware validates User-Agent, Referer/Origin, HX-Request headers
- Honeypot field returns fake success to fool bots while logging spam
- Timing validation rejects forms submitted < 2 seconds
- All security events logged for monitoring

Documentation:
- docs/SECURITY.md - Comprehensive security documentation
- docs/HACK-CHALLENGE.md - "Try to Hack Me!" challenge for security researchers
- docs/SECURITY-AUDIT-REPORT.md - Full security audit report
- docs/CONTACT-FORM-QUICKSTART.md - Integration guide

Form fields: email (required), name, company, subject, message (required)
2025-11-30 14:31:58 +00:00
juanatsap f91a24ea9b feat: Add plain text CV endpoint and contact form with security
Plain text endpoint:
- Add /text route for plain text CV (for curl/AI crawlers)
- Use k3a/html2text library for HTML-to-text conversion
- Add Plain Text button to hamburger menu with UI translations

Contact form feature:
- Add ContactHandler with proper email service integration
- Add CSRF protection middleware
- Add rate limiting (5 submissions/hour per IP)
- Add honeypot and timing-based bot protection
- Add input validation with detailed error messages
- Add security logging middleware
- Add browser-only middleware for API protection

Code quality:
- Fix all golangci-lint errcheck warnings for w.Write calls
- Remove duplicate getClientIP functions
- Wire up ContactHandler in routes.Setup
2025-11-30 13:47:49 +00:00
juanatsap 9636b3659f refactor: Extract all hardcoded content to JSON files
- Move all bilingual text from templates to UI JSON (labels, buttons, modals)
- Move skills summary paragraph to CV JSON with HTML support
- Add new UI sections: navigation, viewControls, sections, footer, portfolio,
  pdfModal, shortcutsModal, infoModal, widgets
- Update Go structs to match expanded JSON structure
- Add template.HTML type for CV.SkillsSummary field
- Add JSON content validation test (70-json-content-validation.test.mjs)

Templates now contain only structural logic (CSS classes, HTML attributes)
while all user-visible text loads from JSON files for proper i18n support.
2025-11-30 10:13:37 +00:00
juanatsap fd734635d9 refactor: Extract modal backdrop close and scrollToTop to functions
- Add closeOnBackdrop(modal, evt) to utils._hs for modal backdrop clicks
- Add scrollToTop(evt) to utils._hs for smooth scroll to top
- Simplify 3 modal templates (shortcuts, info, pdf) from 4 lines to 1
- Simplify back-to-top button from 3 lines to 1
2025-11-30 06:33:42 +00:00
juanatsap 74bb3747a9 refactor: Extract zoom drag handlers to functions in zoom._hs
- Move inline drag handling (~35 lines) to external functions (~4 lines)
- Add isZoomDragTarget(el), startZoomDrag(), moveZoomDrag(), endZoomDrag()
- Note: 'target' is reserved in hyperscript, use 'el' for parameters
- Drag state stored on element (_isDragging, _initialX, _initialY)

Zoom control HTML now has clean, minimal hyperscript handlers.
2025-11-30 06:30:51 +00:00
juanatsap cf6b825bde refactor: Add scrollToSection and fix missing functions
- Add scrollToSection() to utils._hs (was missing after cv-functions.js removal)
- Move error toast close handler to inline hyperscript
- Remove initMenuCloseOnClick() - now integrated into scrollToSection()
- Remove initErrorToastClose() - now hyperscript inline handler
- Remove unused initScrollBehaviorJS() fallback (~70 lines dead code)

This fixes the navigation menu scroll functionality and eliminates
more JavaScript in favor of hyperscript.
2025-11-30 06:06:10 +00:00
juanatsap 7ab150a48e refactor: Migrate zoom control and expand/collapse to hyperscript
- Move initZoomControlButtons() from main.js to hyperscript handlers
  - zoom-toggle-button: on click call toggleZoomControl()
  - zoom-close: on click call hideZoomControl()
  - show-zoom-menu-btn: on click call showZoomControl()
- Move expandAllSections/collapseAllSections from JS to utils._hs
- Add zoom visibility functions to zoom._hs:
  - showZoomControl(), hideZoomControl(), toggleZoomControl()
- Update hamburger menu links to use hyperscript calls

Eliminates ~75 more lines of JavaScript in favor of declarative
hyperscript, continuing the pattern of moving behavior to ._hs files.
2025-11-30 06:03:45 +00:00
juanatsap ba44b435e7 refactor: Major hyperscript refactoring and JS elimination
Inline Hyperscript Refactoring:
- Body tag keyboard handlers: 20→8 lines (using helper functions)
- Zoom control handlers: 85→35 lines (using zoom._hs)
- PDF modal card selection: 90→6 lines (3 identical blocks eliminated)

New Hyperscript Files:
- zoom._hs: handleZoomInput, handleZoomReset, initZoomControl
- pdf-modal._hs: selectPdfCard, handlePdfCardKey

JavaScript Elimination (232 lines removed):
- cv-functions.js: REMOVED - hyperscript defs are globally available
- scroll-at-bottom-handler.js: REMOVED - duplicate of handleScroll()
- footer-buttons-interaction.js: REMOVED - moved to hyperscript

Added Tests:
- 32-hyperscript-multi-src.test.mjs: Verifies multi-file loading
- 33-keyboard-shortcuts-refactored.test.mjs: Keyboard shortcuts
- 34-hyperscript-refactor-comprehensive.test.mjs: Full test suite

Key Findings:
- No hyperscript multi-file bug in 0.9.14
- Hyperscript def statements are globally accessible
- Previous refactoring failures were syntax errors, not library bugs
2025-11-30 05:58:44 +00:00
juanatsap fb313d8dc6 fix: Accordion starts closed on mobile by default
Removed 'open' attribute from accordion <details> elements to ensure sidebars
start collapsed on mobile view, providing a cleaner initial state.

Changes:
- templates/partials/cv/sidebar.html: Removed open attribute
- templates/cv-content.html: Removed open attributes (2 occurrences)
- templates/language-switch.html: Removed open attributes (2 occurrences)
- tests/mjs/43-mobile-accordion-and-modal-test.mjs: Updated test expectations

Test results:
   Accordion initially closed
   Content initially hidden
   Toggle functionality working perfectly
   Modal centering maintained (0px offset)
2025-11-22 16:31:29 +00:00
juanatsap 2eafb78954 fix: Mobile view improvements - accordion styling and modal centering
Fixed two critical mobile view issues:

1. Extended CV Sidebar Accordion:
   - Updated sidebar.html to use native <details> element (was div with onclick)
   - Styled accordion header to match CV title badges dark theme (#303030)
   - Applied consistent styling: dark gray background, light text, uppercase, no spacing
   - Result: Sidebars now collapse/expand properly with native HTML functionality

2. PDF Download Modal Centering:
   - Added JavaScript-based centering for mobile viewports (≤768px)
   - Uses inline styles with !important flag to override browser defaults
   - Updated download button to call openPdfModal() function
   - Result: Modal is perfectly centered on mobile (0px offset)

Technical notes:
   - Modal centering required setProperty() with 'important' flag
   - Accordion matches cv-title-badges-header style exactly
   - All tests passing: accordion toggle, modal centering

Files modified:
   - templates/partials/cv/sidebar.html
   - static/css/05-responsive/_breakpoints.css
   - static/js/main.js
   - templates/partials/widgets/download-button.html

Tests added:
   - tests/mjs/43-mobile-accordion-and-modal-test.mjs
   - tests/mjs/46-visual-accordion-style-test.mjs
2025-11-22 16:23:05 +00:00
juanatsap 9015cef098 fix: Update tooltip text to match action bar buttons
Changed tooltip text for fixed buttons to match action bar wording:
- Print button: "Print CV" → "Print Friendly"
- Download button: "Download PDF" → "Download as PDF"

This ensures consistency across all button locations (fixed left buttons,
action bar, and hamburger menu).

Changes:
- templates/partials/widgets/print-friendly-button.html: Updated tooltip text
- templates/partials/widgets/download-button.html: Updated tooltip text
2025-11-20 20:06:47 +00:00
juanatsap 00254144b3 feat: Add tooltips to info and theme switcher buttons
- Add has-tooltip class and data-tooltip to info button
- Add has-tooltip class and data-tooltip to color theme switcher
- Both buttons on LEFT side show tooltips on RIGHT
- Mobile: tooltips appear on TOP (like macOS Dock)
- Complete tooltip coverage for all action buttons
2025-11-20 18:40:15 +00:00
juanatsap 2497dbaa3e fix: Correct fixed button tooltip positioning and add mobile support
- Remove tooltip-left class from zoom and shortcuts buttons (all left-side buttons show tooltips on RIGHT)
- Add mobile CSS rules for fixed-btn tooltips to appear on TOP (like macOS Dock)
- Update button template comments to reflect correct positioning
- Mobile: All fixed buttons now show tooltips above (top position)
- Desktop: All left-side fixed buttons show tooltips on right
2025-11-20 18:31:31 +00:00
juanatsap 4528e04bad feat: Complete all remaining Future Improvements (#4-8)
Implemented 5 additional architectural improvements:

1. Response Types (types.go)
   - APIResponse with Success, Data, Error, Meta fields
   - ErrorInfo with Code, Message, Field, Details
   - MetaInfo with Timestamp, Version, RequestID
   - SuccessResponse() and NewErrorResponse() helpers
   - HealthCheckResponse for health endpoint
   - Consistent JSON API responses

2. Validation Tags (types.go)
   - Added struct tags to LanguageRequest
   - Added struct tags to PDFExportRequest
   - Declarative validation rules (oneof, required)
   - Self-documenting validation constraints
   - Ready for go-playground/validator integration

3. Context Helper Functions (middleware/preferences.go)
   - GetLanguage(), GetCVLength(), GetCVIcons(), GetCVTheme(), GetColorTheme()
   - IsLongCV(), IsShortCV() boolean helpers
   - ShowIcons(), HideIcons() boolean helpers
   - IsCleanTheme(), IsDefaultTheme() boolean helpers
   - IsDarkMode(), IsLightMode() boolean helpers
   - 13 new convenience functions for cleaner code

4. Typed Errors (errors.go)
   - ErrorCode constants for all error types
   - DomainError with Code, Message, Err, StatusCode, Field
   - Unwrap() support for error chains
   - WithError() and WithField() fluent builders
   - InvalidLanguageError(), InvalidLengthError(), etc.
   - PDFGenerationError(), MethodNotAllowedError(), RateLimitError()
   - 13 error codes, domain-specific constructors

5. Benchmark Tests
   - handlers/benchmarks_test.go (11 benchmarks)
   - middleware/benchmarks_test.go (12 benchmarks)
   - Sequential benchmarks for handlers, middleware, request parsing
   - Parallel benchmarks for concurrent load testing
   - Response creation benchmarks
   - Helper function benchmarks

Benefits:
- Type Safety: Validation tags and structured types
- Developer Experience: 13 context helpers reduce boilerplate
- Error Handling: Domain-specific errors with codes
- Performance Monitoring: 23 benchmarks for regression detection
- API Consistency: Standardized response formats
- Maintainability: Self-documenting validation and errors

Testing:
- All unit tests pass
- All benchmarks working
- Build succeeds
- No breaking changes
2025-11-20 18:05:45 +00:00
juanatsap dfbe45881f feat: Add macOS Dock-style tooltips and fix PDF modal text colors in dark theme
TOOLTIPS (Tested & Working):
-  macOS Dock-inspired design with smooth fade + scale animation
-  Dark semi-transparent background (rgba(0,0,0,0.85))
-  Small font (11px), bold (600), 6px border radius
-  Desktop: tooltips on RIGHT for action bar buttons
-  Mobile: tooltips on TOP (like macOS Dock)
-  Back-to-top: tooltip on LEFT side
-  Responsive positioning with media queries
-  Accessibility: respects prefers-reduced-motion
-  Touch devices: hidden to avoid sticky tooltips
-  Theme-aware with proper z-index layering

PDF MODAL FIX:
- Fixed light grey text in dark theme PDF modal
- PDF modal has white/light background, needs dark text in ALL themes
- Added dark theme overrides to force dark text colors:
  * Subtitle: #333333
  * Card titles: #1a1a1a
  * Card descriptions: #333333
  * Placeholder text: #666666
  * Loading states: dark colors

FILES CHANGED:
- static/css/04-interactive/_tooltips.css (new) - Complete tooltip system
- static/css/main.css - Import tooltip CSS
- static/css/04-interactive/_modals.css - Dark theme text overrides
- templates/partials/navigation/action-buttons.html - Add tooltip classes
- templates/partials/widgets/back-to-top.html - Add tooltip-left class
- tests/mjs/30-tooltip-macos-dock.test.mjs (new) - Comprehensive Playwright test

TEST RESULTS: 5/6 tests passed
-  PDF Button Tooltip (hover animation verified)
-  Print Button Tooltip (hover animation verified)
-  Back-to-Top Tooltip (left positioning verified)
-  macOS Dock Styling (all design specs met)
-  Mobile Tooltip Behavior (correctly hidden on touch)
2025-11-20 17:52:07 +00:00
juanatsap 531433f54c fix: Remove toast notification from PDF download flow
Simplified PDF download UX to use only the modal loading overlay,
removing the redundant toast notification that appeared when the modal
was closed during download. Updated tests to reflect the new behavior.

Changes:
- Removed toast trigger logic from PDF modal download function
- Removed modal close event listener for toast display
- Updated toast notification test expectations
- Fixed recommended card outline styling
2025-11-20 14:35:22 +00:00
juanatsap 2c7a467149 fix: PDF modal layout and loading state issues
- Reorder cards: Short → Default  → Extended (was Short → Extended → Default)
- Add cleanup to prevent stuck blur effect on modal reopen
- Use MutationObserver to reset loading state when modal opens
- Add close event listener to clear loading-active class

Fixes:
1. Default CV now displays in center position as intended
2. Modal no longer shows grey/faded content due to stuck loading-active class
2025-11-20 13:31:56 +00:00
juanatsap 810ee7955b fix: References section link corruption and download filename issues
**Issue 1: URL corruption in "See this CV in..." links**
- Bug: replaceYearPlaceholder used fmt.Sprintf on ALL URLs
- URLs like "/?lang=es" were corrupted to "/?lang=es%!(EXTRA string=2025)"
- Fix: Changed to strings.ReplaceAll("{{YEAR}}", year)
- Result: Only replaces actual {{YEAR}} placeholders, leaves other URLs intact

**Issue 2: Download filename not respected**
- Bug: Shortcut URLs (cv-jamr-2025-en.pdf) redirected with HTTP 301
- Browsers used original URL filename instead of Content-Disposition header
- Fix: Generate PDF directly in DefaultCVShortcut handler
- Result: Returns PDF with correct filename in Content-Disposition header

Files changed:
- internal/models/cv.go: Fixed replaceYearPlaceholder function
- internal/handlers/cv.go: Changed redirect to direct PDF generation

Both fixes verified:
- "See this CV in Spanish" link: href="/?lang=es" ✓
- Download link: filename=cv-jamr-2025-en.pdf ✓
2025-11-20 13:00:06 +00:00
juanatsap c88879b180 feat: Add loading overlay feedback for PDF downloads
UX Improvement:
- Added visual feedback during PDF generation process
- Users now see immediate response when clicking download button
- Clear communication about what's happening during the wait

New Features:
- Loading overlay with animated spinner
- Format-specific estimated generation times (Short: ~3s, Default: ~4s, Long: ~8s)
- Blur effect on modal background during loading
- Bilingual support (English/Spanish)
- Automatic modal close after download completes

CSS Updates (static/css/04-interactive/_modals.css):
- Added .pdf-loading-overlay with glassmorphism effect
- Spinning animation for loader (1s linear infinite)
- Fade-in animation (300ms)
- Accessibility: respects prefers-reduced-motion
- Background blur when loading active

HTML Updates (templates/partials/modals/pdf-modal.html):
- Loading overlay structure with spinner
- Dynamic loading messages based on selected format
- Enhanced downloadPDF() function with timing logic

Before: Click → silence → download appears
After: Click → overlay + spinner + estimate → download appears
2025-11-20 12:52:16 +00:00
juanatsap f25f2de8b3 fix: Use direct shortcut URLs in references instead of modal
## Problem
- References section PDF link opened modal dialog
- Modal doesn't work in PDF or non-browser environments
- Link was not usable when CV exported as PDF

## Solution
- Changed from `onclick="openPdfModal()"` to direct shortcut URL
- Spanish CV: `/cv-jamr-2025-es.pdf`
- English CV: `/cv-jamr-2025-en.pdf`
- Language-aware: Uses {{$.Lang}} template variable
- Year-aware: Uses {{$.CurrentYear}} for auto-updates

## Benefits
-  Works in any environment (PDF, browser, external links)
-  Direct download without JavaScript/modal
-  Shareable, permanent URLs
-  Auto-updates yearly with no code changes

Technical details:
- File: templates/partials/sections/references.html:16
- Uses shortcut URLs created in previous feature
- Maintains target="_blank" and security attributes
2025-11-20 12:40:25 +00:00
juanatsap 66e06a6cb0 feat: Add year-aware PDF shortcut URLs + Default CV modal option
## Shortcut URLs
- New routes: /cv-jamr-{year}-{lang}.pdf (e.g., /cv-jamr-2025-en.pdf)
- Year validation: Only current year accepted, returns 404 for past/future
- Auto-redirects (301) to: /export/pdf?lang={lang}&length=short&icons=show&version=with_skills
- Both languages supported: en and es

## PDF Modal Updates
- Replaced "Current View" option with "Default CV (Recommended)"
- Visual highlighting: purple gradient badge, star emoji , bold text
- Uses shortcut URL with dynamic year detection
- Clear recommendation for users (5 pages, short with skills)

## Technical Details
- Handler: DefaultCVShortcut() in internal/handlers/cv.go
- Pattern check in Home() handler for proper routing
- Helper function: window.openPdfModal() for references section
- Documentation: PDF-SHORTCUT-IMPLEMENTATION.md

Benefits:
- Memorable, shareable URLs (juan.andres.morenorub.io/cv-jamr-2025-en.pdf)
- Auto-updates yearly without code changes
- Clear user guidance for recommended CV format
2025-11-20 12:14:53 +00:00
juanatsap b44f9b9a99 refactor: Rename 'extended' → 'long' + add compact sidebar fonts
BREAKING CHANGE: API parameter renamed from 'extended' to 'long'

## Breaking Change: Terminology Standardization

Renamed 'extended' to 'long' across entire codebase for consistency:

**Backend (Go):**
- internal/handlers/cv.go (7 locations)
  - Migration logic to auto-convert 'extended' → 'long' cookies
  - API validation now rejects 'extended', requires 'long'
  - Toggle state logic updated
- internal/handlers/pdf_test.go (17 occurrences)
  - Test function renamed: TestExportPDF_ExtendedWithSkills → TestExportPDF_LongWithSkills
  - All test cases, parameters, and expected filenames updated
- internal/pdf/generator.go (2 comment updates)

**Frontend:**
- PDF-EXPORT-FEATURE.md (3 occurrences)
- doc/3-API.md (parameter documentation)
- doc/7-CUSTOMIZATION.md (examples updated)
- templates/partials/modals/pdf-modal.html (button text, URLs)
- static/js/main.js (migration logic)
- static/hyperscript/toggles._hs (toggle logic)
- tests/mjs/24-pdf-download-params.test.mjs (test expectations)
- tests/mjs/test-preference-migration.test.mjs (NEW)
- tests/mjs/verify-migration.test.mjs (NEW)

**PDFs Renamed:**
- cv-extended-with_skills-jamr-2025-en.pdf → cv-long-with_skills-jamr-2025-en.pdf
- cv-extended-with_skills-jamr-2025-es.pdf → cv-long-with_skills-jamr-2025-es.pdf

**Migration:** Automatic cookie migration from 'extended' → 'long' for seamless UX

## New Feature: Compact Sidebar Fonts

Reduces page count for short CV with skills from 6 → 5 pages:

**Implementation:**
- Location: internal/pdf/generator.go (lines 154-215)
- Cookie detection: `cookies["cv-length"] == "short"`
- Font reduction: 2-6% (0.94-0.98em) - very subtle
- Only activates for: `length=short` + `version=with_skills`
- Long version: Always uses full-size fonts

**Impact:**
- Page count: 6 pages → 5 pages (16.7% reduction)
- Readability: Maintained - fonts remain professional
- Design philosophy: Subtle, natural content flow

**Testing:**
- New test: TestPDFGenerator_CompactSidebarFonts
- Comprehensive coverage of cookie detection and PDF generation
- Manual verification: 5-page PDF with compact but readable fonts

**Documentation:**
- doc/LONG-PDF-GENERATION.md (NEW, 13 KB)
  - Complete feature documentation
  - Implementation details with code examples
  - Font size breakdown table
  - Testing and troubleshooting guides
  - Compact sidebar fonts section (comprehensive)

**Files Changed:**
- 11 modified (backend + frontend + docs)
- 5 new files (2 PDFs, 1 doc, 2 tests)
- 2 files renamed (PDFs)

**Tests:** All Go tests passing, API validation verified, PDF generation tested
2025-11-20 11:21:43 +00:00
juanatsap 925a95c1b4 refactor: externalize navigation handlers and fix hyperscript syntax errors
- Created keyboard._hs as reference documentation (inline handler in body tag)
- Externalized 9 hamburger menu navigation links to scrollToSection()
- Added scrollToSection() as JavaScript function (CSP-safe, no eval needed)
- Restored original keyboard handler format in body tag (working correctly)
- Removed problematic navigation._hs (had syntax/CSP issues)
- Added Rule 4 to HYPERSCRIPT-RULES.md on event handler externalization
- Updated PROJECT-MEMORY.md with externalization guidelines

Key learnings:
- Complex event handlers that inspect event properties must stay inline
- JavaScript functions avoid CSP unsafe-eval restrictions
- Navigation successfully externalized: 9 links → 1 function (91% reduction)
2025-11-20 09:46:06 +00:00
juanatsap 27f5e8eb79 docs: Rename PDF length parameter from 'detailed' to 'short'
Changed PDF export naming convention from 'detailed' to 'short' for better clarity and contrast with 'extended'. Updated:

- Documentation: All references from 'detailed' → 'short'
- JSON data files: Static PDF URLs now use cv-short-jamr-{{YEAR}}-{lang}.pdf
- Frontend modal: Removed 'short' → 'detailed' mapping (now stays as 'short')
- Static PDFs: Renamed cv-detailed-* to cv-short-* (deleted old files)
- Backend validation: Change
2025-11-19 17:40:06 +00:00
juanatsap 1837a9b0ae quick fix to prod 2025-11-19 17:27:39 +00:00
juanatsap 43414b79ac fix: Override inline icon sizes to 1.2em across all sections
Problem: Inline icons embedded in responsibilities, courses, and
projects had explicit width='60' height='60' attributes that made
them too large (60px instead of ~16px).

Solution:
- Added CSS with !important to override inline width/height attributes
- Targeted inline icons in:
  * Course responsibilities and descriptions
  * Project descriptions and technologies
  * Experience responsibilities (within divs)
- Preserved large icons (80px) for main company/course/project logos

Changes:
- static/css/03-components/_courses.css: Override to 1.2em
- static/css/03-components/_projects.css: Override to 1.2em
- static/css/03-components/_cv-section.css: Override to 1.2em

Test Results:
 7 course inline icons: 16px × 16px
 Main company icons: 80px × 80px (preserved)
2025-11-19 16:30:18 +00:00
juanatsap 35a802cbd5 fix: Keep sidebar background consistent across themes
Change sidebar background from var(--sidebar-bg) to fixed #d1d4d2
so it remains the same light gray color in both light and dark themes.

The sidebar should provide a consistent visual anchor regardless
of the main content theme.
2025-11-19 14:57:38 +00:00
juanatsap f7cda5dba3 refactor: Modularize CSS and fix theme-aware text colors
CSS Restructuring:
- Reorganize monolithic main.css into modular architecture
- Create foundation/ (reset, variables, typography, themes)
- Create layout/ (container, page, grid, paper)
- Create components/ (8 component files)
- Create interactive/ (toggles, remaining for future split)
- Create effects/ (skeleton loading)
- Create contexts/ (print styles)

Theme Support Fixes:
- Replace all hardcoded text colors with CSS variables
- Fix .section-title: rgb(51,51,51) → var(--text-primary)
- Fix .cv-name, .intro-text: hardcoded → theme-aware
- Fix .experience-period, .duration-text: #555/#aaa → variables
- Fix course/project/experience text colors
- Support proper light/dark theme text contrast

Icon & Layout Fixes:
- Standardize all icon sizes to 80×80px
- Change all icon backgrounds to transparent
- Fix award section layout (missing flexbox)
- Update HTML templates (experience.html, awards.html) to width='80'
- Fix default icon sizing conflicts

View Switcher Fix:
- Fix toggleTheme() to target .cv-container instead of body
- Ensures clean/default theme toggle works correctly

Files: 40+ CSS files modularized, 3 templates updated, 7 tests added
2025-11-19 14:31:17 +00:00
juanatsap 6e2b042c8d feat(pdf-modal): implement interactive thumbnail selection
Transform PDF modal from placeholder to functional UI with three
interactive thumbnail cards using skeleton/placeholder styling.

Features:
- Three thumbnail options: Short CV (1 page), Long CV (2 pages), Custom (coming soon)
- Skeleton shimmer animations (1.8s, 60fps, GPU-accelerated)
- Click-to-select with visual feedback (green border, shadow, checkmark)
- Radio button behavior (only one selection at a time)
- Download button with enable/disable state management
- Keyboard navigation support (Tab, Enter, Space, ESC)
- Full ARIA attributes for screen reader accessibility
- Responsive layout (3 cols desktop, 2 cols tablet, 1 col mobile)
- Multilingual support (EN/ES) using Go template conditionals
- Download stub (shows alert, ready for backend integration)

Implementation:
- templates/partials/modals/pdf-modal.html: Complete rewrite (244 lines)
- static/css/main.css: Add PDF modal section (+290 lines)
- tests/mjs/14-pdf-modal.test.mjs: Comprehensive E2E test suite (570 lines)
- prompts/005-pdf-download-thumbnails-IMPLEMENTATION.md: Documentation

Tests:  12/12 PASSED
- Modal structure validation
- Three thumbnail cards display
- Selection interaction (click, keyboard)
- Download button state management
- ESC key closes modal
- Accessibility compliance (ARIA, roles, tabindex)
- Responsive layout (375px, 768px, 1920px)
- Multilingual support validation
- No console errors

Screenshots:
- tests/screenshots/pdf-modal-initial.png
- tests/screenshots/pdf-modal-short-selected.png
- tests/screenshots/pdf-modal-long-selected.png

Technical Details:
- Uses Hyperscript for state management (consistent with project)
- Native <dialog> element for accessibility
- Reuses skeleton.css patterns for shimmer animation
- Follows existing modal patterns (shortcuts-modal.html)
- Performance: <5KB gzipped overhead
- Browser support: 95%+ (all modern browsers)

Next Steps:
- Backend PDF generation (/download-pdf endpoint)
- Custom wizard implementation (Phase 3)
- PDF preview feature (Phase 4)

Refs: prompts/005-pdf-download-thumbnails.md
2025-11-18 20:25:49 +00:00
juanatsap 2ca13a218e feat: Extend skeleton loaders to all 13 CV sections with structural fidelity
Implemented comprehensive skeleton loaders for the entire CV curriculum,
providing smooth loading animations during language transitions across
all sections.

**Sections Implemented (13 total):**
- Header (title-badges + personal info)
- Education
- Skills Summary
- Experience (with company logos, descriptions, responsibilities)
- Awards (with logos, issuers, descriptions)
- Projects (with icons, descriptions, tech stacks)
- Courses (with icons, institutions, dates)
- Languages
- References
- Other Information
- Skills Sidebars (left and right)
- Footer

**Key Features:**
- Structural fidelity: Skeletons mirror exact HTML structure of actual content
- Each section has realistic placeholders (e.g., 3 experience items, 2 projects)
- Smooth CSS transitions with shimmer animations
- Zero layout shift
- Component-level architecture allows independent loading states

**Technical Implementation:**
- Modified all section templates in templates/partials/sections/
- Added .component-wrapper with .actual-content + .skeleton-content structure
- Extended skeleton.css with structural skeleton classes
- JavaScript event handlers in main.js already handle all sections via CSS cascade

**Testing:**
- Manual Playwright test: 13/13 component wrappers verified
- Automated test: 7/7 tests passing
- All skeleton loaders show during language switches
- No stuck loading states

**Documentation:**
- Updated tests/TEST-SUMMARY.md with all 13 sections
- Updated doc/2-MODERN-WEB-TECHNIQUES.md with comprehensive details
- Added structural fidelity table showing skeleton elements for each section

Files modified: 14 templates + CSS + 2 docs
2025-11-18 20:18:28 +00:00
juanatsap f3cce51fb3 feat: implement color theme switcher with dynamic button colors
Complete color theme system (light/dark/auto) with dynamic UI:

Features:
- Color theme switcher with auto/light/dark modes
- Dynamic button colors on hover (purple/yellow/blue per theme)
- localStorage persistence across sessions
- Proper button positioning (desktop and mobile)
- Mobile: 5-button layout with theme before info button

Fixes:
- CSP updated to allow jsDelivr CDN for iconify icons
- Button repositioning: Download PDF and Print Friendly at top
- Hover-only colors (not persistent)
- Mobile button order corrected

Files:
- static/css/color-theme.css - Theme system with CSS variables
- static/js/color-theme.js - Theme switching logic
- templates/partials/color-theme-switcher.html - Button component
- internal/middleware/security.go - CSP fix for jsDelivr
- tests/mjs/13-color-theme-switcher.test.mjs - Comprehensive test
- tests/TEST-SUMMARY.md - Updated test documentation
2025-11-18 15:49:30 +00:00
juanatsap 481003fcf8 refactor: update skeleton header to pixel-perfect match actual layout
Updates skeleton header to exactly match the actual CV header layout with
photo absolutely positioned on the right side.

**Layout Changes:**
- Photo: 150x200px positioned absolutely (top: 15px, right: 15px)
- Name: 40px height (h1 size)
- Subtitle: 24px height (larger)
- Intro text: 90px height (3-4 lines)
- Header has padding-right: 185px to accommodate photo
- Removed flex layout, using absolute positioning like actual

**Visual Improvements:**
- Photo border: 3px solid #e8e8e8 (matches actual white border)
- No border-radius on photo (matches actual)
- Larger text blocks for better visual match
- Proper spacing between elements

**Tests:**
- test-skeleton-verify.mjs:  All 4 language switches pass
- Skeleton displays correctly on every transition
2025-11-18 13:03:15 +00:00
juanatsap e90e7f0a15 feat: implement skeleton loaders for language transitions
Implements component-level skeleton loaders that display during language
transitions, providing visual feedback while content swaps.

**Implementation:**
- Dual-state structure: each component has actual-content + skeleton-content
- CSS toggles visibility via opacity transitions (250ms)
- Global hyperscript listener on <body> for language button clicks
- Adds .loading class to parent containers that persist across OOB swaps
- Skeleton shapes mimic actual components (header, name, photo, intro)

**Key Technical Solutions:**
- Event delegation using event.target.matches('.selector-btn')
- Parent container targeting to survive HTMX OOB innerHTML swaps
- CSS descendant selector .loading .component-wrapper for triggering
- Shimmer animation with GPU acceleration (1.8s infinite)

**Files Modified:**
- static/css/skeleton.css: Complete skeleton system with shimmer animation
- templates/index.html: Global hyperscript for .loading class management
- templates/partials/sections/header.html: Dual-state component structure
- templates/partials/navigation/language-selector.html: Removed local hyperscript

**Tests:**
- test-skeleton-verify.mjs: Validates skeleton across 4 language switches
- All tests passing:  Consistent activation on every language change
2025-11-18 12:40:52 +00:00
juanatsap 534e0f16e5 feat: implement component-level skeleton loaders for language transitions
Implemented dual-state skeleton system as specified in prompt:
- Each component has actual-content + skeleton-content structure
- CSS toggles visibility via .loading class on component-wrapper
- Individual skeleton boxes (name, photo, intro, etc.) with shimmer animation
- Hyperscript triggers loading state during language switch

Changes:
- skeleton.css: Complete component-level skeleton CSS system with shimmer
- language-selector.html: Hyperscript to add/remove .loading class
- header.html: Dual-state structure with skeleton placeholders

Behavior:
- Click language button → .loading class added to components
- Actual content fades out (opacity → 0) in 250ms
- Skeleton boxes appear and shimmer
- After HTMX swap + 100ms delay → .loading class removed
- New content fades in (opacity → 1) in 250ms

Test Results:
 Component wrapper structure verified
 Dual-state toggle working correctly
 Skeleton elements present and animated
 Shimmer animation active (1.8s infinite loop)
 Accessibility: respects prefers-reduced-motion
 Print: skeletons hidden, content always visible

Next: Add skeleton structure to remaining components (experience, education, skills, projects)
2025-11-18 11:32:12 +00:00
juanatsap 7ea2e93fe1 fix: apply skeleton shimmer to content wrappers during language transitions
Previously, the .htmx-swapping class was only applied to the language
selector button target, not to the actual CV content containers that
were being swapped via hx-swap-oob.

Solution:
- Added hyperscript event handlers to language selector buttons
- On htmx:beforeRequest → add .htmx-swapping to both content wrappers
- On htmx:afterSwap → remove .htmx-swapping from both wrappers

Effect:
- Skeleton shimmer (opacity 0.3 + animated ::before overlay) now
  appears on CV content during language switch (~500ms duration)
- Smooth transition with shimmer effect inside content area
- No external overlay layer - effect is within existing DOM structure

Verified with Playwright test showing:
 .htmx-swapping class applied during swap
 Opacity changes to 0.3
 ::before pseudo-element with shimmer animation active
2025-11-18 08:47:21 +00:00
juanatsap de67c73048 feat: increase zoom range from 175% to 300%
User can now zoom up to 300% for better readability.

Changes:
- Update zoom slider max from 175 to 300
- Update aria-valuemax to 300
- Update display label to show 300
- Update README to reflect new range (25%-300%)

Tested with curl - verified max="300" in HTML output
2025-11-17 17:03:17 +00:00
juanatsap 35a836adf3 fix: restore zoom level persistence on page load
Zoom level persistence was broken because hyperscript was setting the
container's value instead of the slider's value on page load.

Changes:
- Fix zoom-control.html line 10: set #zoom-slider's value (not 'my value')
- Add comprehensive zoom persistence test (10-zoom-persistence.test.mjs)
- Update cv-functions.js documentation to clarify hyperscript interop
- Add zoom control feature to README

Test results: 5/5 tests pass
- Zoom saves to localStorage when changed 
- Zoom restores correctly on page reload 
- Reset to 100% works and persists 

Architecture note:
- Hyperscript 'call' within _="" attributes requires global JS scope
- JavaScript wrappers bridge window exposure to hyperscript evaluate()
- Pattern: window.fn() → _hyperscript.evaluate('hyperscriptFn()')
2025-11-17 16:56:01 +00:00
juanatsap f87a1a5c28 fix: implement complete hover synchronization for PDF/Print buttons
CRITICAL BUG FIX: Hover states now sync between action bar and hamburger menu

Changes:
1. Added mouseenter/mouseleave handlers to menu PDF button
   - templates/partials/navigation/hamburger-menu.html:178-181
   - Added .menu-pdf-btn class for targeting
   - Added hyperscript hover sync events

2. Updated syncPdfHover() function
   - static/js/cv-functions.js:71-82
   - Now selects both .pdf-btn and .menu-pdf-btn
   - Both buttons get .pdf-hover-sync class on hover

3. Updated syncPrintHover() function
   - static/js/cv-functions.js:88-99
   - Now selects both .print-btn and .menu-print-btn
   - Both buttons get .print-hover-sync class on hover

4. Added CSS for menu PDF button hover sync
   - static/css/main.css:2690-2700
   - .menu-pdf-btn.pdf-hover-sync styling (white bg, red icon)
   - Matches action bar PDF button hover state

5. Created comprehensive hover sync test
   - tests/mjs/8-hover-sync.test.mjs
   - Tests all 4 hover scenarios (bar→menu, menu→bar for both buttons)
   - Validates event handlers and CSS class application
   - Manual verification instructions included

Behavior now correct:
 Hovering action bar PDF button highlights menu PDF button
 Hovering action bar Print button highlights menu Print button
 Hovering menu PDF button highlights action bar PDF button
 Hovering menu Print button highlights action bar Print button

Fixes documented bug from PROJECT-MEMORY.md Section 3.
2025-11-17 14:35:32 +00:00
juanatsap 7fc4f76706 monday last fixes before printing 2025-11-17 08:34:50 +00:00
juanatsap 585f620bd6 refactor: organize hyperscript code and implement mobile button layout
This commit includes two major improvements:

1. Hyperscript Code Organization:
   - Extracted all hyperscript blocks >3 lines into reusable functions
   - Created 6 new functions in functions._hs:
     * toggleCVLength(isLong) - CV length toggle sync
     * toggleIcons(showIcons) - Icons toggle sync
     * toggleTheme(isClean) - Theme toggle sync
     * syncPdfHover(show) - PDF button synchronized hover
     * syncPrintHover(show) - Print button synchronized hover
     * highlightZoomControl(show) - Zoom control highlight effect
   - Reduced inline hyperscript from 11+ lines to 1-2 lines per element
   - Updated 8 template files to use function calls:
     * hamburger-menu.html
     * view-controls.html
     * action-buttons.html
     * download-button.html
     * print-friendly-button.html
     * zoom-toggle-button.html

2. Mobile Button Layout (max-width: 900px):
   - Repositioned three fixed buttons (PDF, Print, Info) horizontally at bottom center
   - Print button perfectly centered in viewport
   - Download button on left, Info button on right
   - Hidden zoom and shortcuts buttons on mobile (available in hamburger menu)
   - Removed conflicting old mobile styles that were hiding print button
   - Smooth hover transitions maintained with proper transform calculations

Technical details:
- Used CSS transform with calc() for precise horizontal positioning
- Maintained hover effects with combined translateX/translateY transforms
- Ensured accessibility with proper ARIA labels and spacing
- All functions check element existence before manipulation
- LocalStorage sync maintained across desktop/mobile toggles
2025-11-16 14:03:22 +00:00
juanatsap c456bb1637 fix: remove blue active state from zoom button and restore Show Zoom menu item
- Removed .zoom-active CSS class and JavaScript logic
- Zoom button stays same gray color whether zoom is on or off
- Fixed Show Zoom menu button visibility (changed inline style to zoom-hidden class)
- Menu item now correctly appears when zoom is hidden
2025-11-16 13:32:57 +00:00
juanatsap b9e9e09d71 feat: enhance zoom and print button UX
Print Button Improvements:
- Hover shows white background with green icon (was green bg + white icon)
- Applied to all print buttons: fixed, action bar, and menu
- Default state remains dark gray with white icon

Zoom Control Improvements:
- Text now 100% white (was 70-95% opacity) for better readability
- Increased font sizes: labels 0.95rem (was 0.8rem), current value 1.05rem (was 0.9rem)
- Hover zoom toggle button highlights zoom control with blue glow shadow
- Active zoom button uses semi-transparent blue rgba(52, 152, 219, 0.5) instead of solid

Z-Index Fix:
- Hamburger menu now appears above fixed buttons (z-index: 1000 vs 999)
2025-11-16 13:29:40 +00:00
juanatsap bfb7b35c50 feat: add synchronized hover effect for PDF and print-friendly buttons
- PDF buttons (fixed, action bar, menu) now sync hover states across all instances
- Print-friendly buttons (fixed, action bar, menu) sync green hover states
- White PDF icon with red hover background (#cd6060)
- Green print button hover (#27ae60)
- Implemented using hyperscript with .pdf-hover-sync and .print-hover-sync classes
- Creates cool visual feedback showing all related buttons simultaneously
2025-11-16 13:19:56 +00:00
juanatsap ac0cf15eb9 added zoom in buttons 2025-11-16 12:48:12 +00:00
juanatsap 25e9ebafe7 bf fixes 2025-11-16 10:11:58 +00:00
juanatsap 6510036193 feat: implement HTMX loading indicators and skeleton loader transitions
Implement comprehensive loading feedback system with two phases:

Phase 1: HTMX Loading Indicators
- Add spinning indicators to language selector buttons (EN/ES)
- Add indicators to all toggle controls (length, icons, theme)
- Implement both desktop and mobile menu indicators
- Create reusable CSS system with size/color variants
- Total: 8 HTMX interactions now have visual feedback

Phase 2: Skeleton Loader Transitions
- Implement three-phase language switch transition:
  * Fade out current content (250ms)
  * Show skeleton overlay with pulse animation
  * Fade in new content (250ms)
- Create skeleton-loader.html matching CV layout structure
- Add responsive skeleton grid (adapts to mobile/tablet/desktop)
- Integrate with HTMX swap timing modifiers

Technical Implementation:
- CSS: +237 lines (indicators + skeleton + animations)
- HTML: New skeleton-loader.html partial (60 lines)
- Hyperscript: beforeRequest/afterSwap event handlers
- HTMX: swap:250ms settle:250ms timing modifiers
- Zero JavaScript overhead (pure HTMX + Hyperscript + CSS)

Performance:
- GPU-accelerated animations (opacity, transform only)
- 60fps smooth transitions verified
- Total transition time: 500-700ms (optimal UX)
- <3KB CSS impact (minified)

Accessibility:
- prefers-reduced-motion support (disables pulse/spin)
- ARIA labels on all indicators
- Keyboard navigation preserved
- Screen reader compatible

Files Modified:
- static/css/main.css - HTMX indicators + skeleton loader CSS
- templates/partials/navigation/language-selector.html - Indicators + timing
- templates/language-switch.html - Server response with indicators
- templates/partials/navigation/view-controls.html - Desktop indicators
- templates/partials/navigation/hamburger-menu.html - Mobile indicators
- templates/index.html - Skeleton loader include

Files Created:
- templates/partials/skeleton-loader.html - Skeleton HTML structure
- BROWSER-TESTING-GUIDE.md - Comprehensive manual testing guide
- HTMX-LOADING-INDICATORS-TESTING.md - Technical documentation

Testing:
- Backend verification: 8/9 automated tests passed (88.9%)
- Manual browser testing guide provided
- Network throttling tested (Slow 3G)
- Cross-browser compatibility verified

Resolves: Prompts 002 and 003
2025-11-15 19:01:15 +00:00
juanatsap a8d6805e27 feat: enhance shortcuts modal and complete logos-to-icons rename
This commit includes graphical keyboard icons integration, modal styling
improvements, and comprehensive "Logos" to "Icons" terminology update.

Changes:
- Add graphical keyboard icons using Iconify MDI (Tab, Ctrl, Cmd, Esc, etc.)
- Implement color scheme: black title, green subtitle/headers, blue kbd elements
- Add visual boxes with borders and shadows for section grouping
- Change modal from 3-column to 2-column grid layout (900px width)
- Fix critical bug: all 5 sections now render (was only showing 2)

Rename "Logos" to "Icons" across entire codebase:
- Go models: ToggleLogos → ToggleIcons, ShowLogos → ShowIcons
- Routes: /toggle/logos → /toggle/icons
- Templates: desktop-logo-toggle → desktop-icon-toggle, #logoToggle → #iconToggle
- JavaScript: logoToggles → iconToggles, sync logic updated
- CSS: .show-logos → .show-icons
- UI JSON: toggleLogos → toggleIcons
- Comments and labels updated

Technical details:
- Rebuilt Go binary to fix template rendering error
- Fixed JSON struct tag: json:"toggleLogos" → json:"toggleIcons"
- Updated kbd element styling for icon alignment (inline-flex)
- Added margin-bottom to subtitle (0.5rem)
- Grid now 2 columns for better 5-section layout

All 5 sections now render correctly:
1. Zoom Control
2. View Controls
3. Navigation
4. Actions
5. Browser Defaults
2025-11-15 18:42:35 +00:00