PROBLEM:
- htmx:swapError with "Cannot read properties of null (reading 'insertBefore')" on double-click
- Toggle animations were "digital" (instant snap) instead of "analogical" (smooth slide)
- Conflict between server templates with hx-swap-oob and client-side hyperscript
ROOT CAUSE:
- Server templates returned HTML with hx-swap="outerHTML" + hx-swap-oob="true"
- This destroyed and recreated DOM elements during swap
- Second click tried to insert into null parent (element was destroyed)
- CSS transitions broke because element was destroyed mid-animation
SOLUTION:
- Remove all HTML from toggle templates (length-toggle.html, logo-toggle.html, theme-toggle.html)
- Templates now return empty comment: "<!-- Template not used - toggles use hx-swap="none" with inline hyperscript -->"
- Toggles use hx-swap="none" to prevent any DOM replacement
- All visual updates handled client-side via inline hyperscript
- Server only saves cookies in background (no HTML returned)
BENEFITS:
- ✅ No more null reference errors (no DOM destruction)
- ✅ Smooth CSS transitions work perfectly (element preserved)
- ✅ Desktop/mobile toggles sync via direct ID manipulation
- ✅ Zero HTMX swap conflicts
- ✅ Clean separation: client handles visuals, server persists state
DOCUMENTATION:
- Updated MODERN-WEB-TECHNIQUES.md with Phase 8
- Documented the complete debug journey and solution
- Added architecture pattern for client-first toggles
- Moved menu hover logic from JavaScript to CSS selectors, reducing JS to minimal bridge code
- Replaced JavaScript-based toast timing with pure CSS animation lifecycle (slide in → stay → fade out)
- Removed unnecessary event handlers and legacy compatibility code for cleaner implementation
- Created zoom-wrapper div around cv-container
- Zoom now applies to wrapper only, footer adjusts naturally below
- Footer no longer scaled, stays at normal size
- Fixes gap between content and footer at low zoom levels
- Reduced back-to-top button size (35px default, grows to 50px on hover)
- Cleaner separation of concerns for zoom functionality
- Moved "Zoom" button from action bar to hamburger menu under "Acciones Rápidas"
- Close button (X) now grey/subtle by default (opacity: 0.7)
- Close button turns red only on hover for clear indication
- Updated JavaScript to reference show-zoom-menu-btn instead of show-zoom-btn
- Added preventDefault to showZoomControl to prevent link navigation
- Add close button (X) to zoom control widget
- Make zoom control draggable anywhere on screen
- Persist dragged position in localStorage (cv-zoom-position)
- Add "Zoom" button to action bar to show control when hidden
- Persist visibility state in localStorage (cv-zoom-visible)
- Cursor changes to "move" to indicate draggability
- Interactive elements (slider, buttons) don't trigger drag
- Position stays within viewport bounds
- All features work on desktop only (zoom hidden on mobile)
Previously, HTML in short descriptions was being escaped and displayed
as raw text instead of rendering properly. This happened because the
safeHTML template function had been removed for security reasons.
Changes:
- Added safeHTML function back to template.FuncMap (template.go:53-55)
- Updated three template locations to use safeHTML pipe:
* Experience descriptions (cv-content.html:122)
* Award descriptions (cv-content.html:180)
* Project descriptions (cv-content.html:232)
Security note:
The safeHTML function is safe to use here because CV data comes from
trusted YAML files controlled by the site owner, not user input.
Clear documentation added to prevent misuse with untrusted content.
Examples now rendering correctly:
- Award: "Premio por excelencia en marketing B2B...con <a href=...>Clicplan</a>"
- Projects: Links to Lidering, Jorpack, Delivery Bikes BCN, Mobbeel
Design improvements based on user feedback:
- Move current zoom value inside circular reset button
- Add colorful gradient (red→orange→blue→green) to slider on hover
- Make reset button perfectly circular (44px diameter)
- Dynamic value display updates in real-time inside button
- Maintains gray monochrome when not hovering
- Enhanced visual feedback with smooth color transitions
- Mobile responsive with smaller circular button (38px)
Technical changes:
- HTML: Moved #zoom-value-current span inside button
- CSS: border-radius 50%, min-width/min-height for perfect circle
- CSS: Gradient hover for both WebKit and Firefox sliders
- JavaScript: Already compatible (targets same element ID)
- Move <strong> tags from data to template for proper HTML rendering
- Update both English and Spanish data files to use plain text
- Template now wraps license type in <strong> tags
- Enhanced CI/CD pipeline with coverage reporting, benchmarks, and artifact uploads
- Implemented rate limiter IP validation with proxy support and spoofing protection
- Added extensive Makefile test targets for coverage, benchmarks, and continuous testing
- Expanded middleware chain with request validation, size limits, and suspicious activity logging
- Change from HTMX partial update to full page redirect
- Ensures modal and all UI elements render in correct language
- Uses window.location.replace() to avoid history pollution
- Changed selectLanguage() to reload full page instead of HTMX partial update
- Fixes issue where info modal content stayed in original language
- Ensures all UI elements (including modals) update correctly on language change
- Replace blue border with green curly brackets { } using CSS pseudo-elements
- Add proper spacing and vertical centering with inline-flex
- Add engaging subtext below GitHub button: "Want to know how it's built?"
- Bilingual support for subtext (EN/ES)
- Change sidebar titles from center to left alignment for better readability
- Update badge from "Senior Consultant" to "Technical Consultant"
- Applies to both English and Spanish versions
- Update summary to be concise and focus on high-availability systems
- Replace technology-specific badges with professional role titles
- Add emphasis (bold) to key concepts in Skills section
- Update project metrics (20 websites, 35-40 consulting clients)
- Change "EN VIVO" to universal "LIVE" badge for consistency
- Improve professional positioning: Senior Consultant, Full-Stack Engineer, Authentication Specialist, Solution Architect
PROBLEM:
- Left and right sidebars occupy too much space on mobile
- User wants curriculum (main content) prioritized on mobile
- Sidebars should be collapsed by default, expand only on user interaction
SOLUTION:
HTML Changes (cv-content.html):
- Wrapped each sidebar in accordion structure
- Added .sidebar-accordion-header with brain icon, title, and chevron
* Left sidebar: "Technical Skills" / "Competencias Técnicas"
* Right sidebar: "More Skills" / "Más Competencias"
- Added .sidebar-accordion-content wrapper around sidebar sections
- Added onclick="toggleSidebar(this)" handler
CSS Changes (main.css):
Desktop (default):
- .sidebar-accordion-header: display: none (hidden on desktop)
- .sidebar-accordion-content: always visible (no restrictions)
Mobile @ 900px:
- .sidebar-accordion-header: display: flex, styled as clickable bar
* Padding: 1rem 1.5rem
* Background: var(--sidebar-gray) with hover effects
* Font: 700 weight, 1.1rem size
* Chevron rotates 180deg when active
- .sidebar-accordion-content: collapsed by default
* max-height: 0 (collapsed)
* max-height: 5000px when .active (expanded)
* Smooth transitions: 0.3s ease-out (close) / 0.5s ease-in (open)
* Padding: 0 when closed, 1.5rem when open
- .cv-sidebar: padding: 0 (accordion header handles padding)
JavaScript Changes (index.html):
- Added toggleSidebar(header) function
* Toggles .active class on header and content
* Smooth expand/collapse animation via CSS max-height transition
DATA Changes (cv-en.json, cv-es.json):
- Updated summary with more professional, experience-focused description
- Emphasizes 18+ years experience, international clients, practical solutions
BEHAVIOR:
- Desktop: Sidebars always visible, no accordion headers
- Mobile: Sidebars collapsed by default showing only header bar
- Click header: Expands sidebar with smooth animation
- Click again: Collapses sidebar
- Main content prioritized and always visible
- Each sidebar toggles independently
- Update modal title from "CV 2025 - {JAMR}" to "CV 2025 JAMR -" with inline photo
- Add .info-modal-photo CSS with flexbox layout for proper photo alignment
- Change button text from "View Source Code" to "View Project in Github" (EN/ES)
- Photo styled with green border and shadow matching modal theme
- Changed project status badges from "CURRENT" to "LIVE" with wifi icon for active projects
- Updated CV reference links to direct PDF downloads instead of modal popups
- Marked SAP CDC Demo and Client Projects as currently live/maintained
References section improvements:
- Remove TwenTIC recommendation letter
- Add Megabanner recommendations with author attribution (David Amorós)
- Update Presentation Letter URL to Domestika profile
- Update Chronological CV link to trigger PDF download modal
- Add 'action' field to Reference struct for custom behaviors
Print layout fixes:
- Show experience company logos in print view (40×40px)
- Remove print media query that was hiding .company-logo elements
- Maintain consistency with other logo types (courses, projects, awards)
Technical changes:
- Add Action field to Reference model with omitempty JSON tag
- Implement conditional rendering for downloadPDF action in template
- Links with action="downloadPDF" now call openPdfModal() function
Print CSS enhancements:
- Show ALL icons, logos, and badges by default in print (16px section icons, 40px company/project logos)
- Improved header layout with bigger photo (110x147px, 3:4 ratio) positioned on right
- Name and years right-aligned with justified intro text below
- Maintained flex layout for experience/project items to show logos side-by-side
- Compact badge sizing (7pt font) for print
PDF Download UX:
- Replaced direct download links with modal popup
- Shows work-in-progress message directing users to Print Friendly feature
- Bilingual modal (English/Spanish) matching info modal styling
- Modal closable via backdrop click, X button, or Escape key
- Prevents accidental downloads of outdated PDFs
UI improvements:
- Enhanced icon toggle contrast and visibility
- Consistent modal behavior across info and PDF modals
Comprehensive mobile optimization (≤768px) for improved consistency and readability:
- Center profile photo between name and intro text on mobile
- Unify all logo/icon sizes to 60×60px for visual consistency
- Standardize spacing across experience, courses, projects, and awards
- Reduce font sizes proportionally for better mobile readability
- Remove justified text alignment (except intro/skills) to prevent awkward spacing
- Apply consistent 1rem gaps and 1.5rem padding throughout
- Optimize sidebar items with reduced margins and font sizes
Technical improvements:
- Add comprehensive mobile breakpoint rules at 768px
- Implement flexible photo positioning (absolute on desktop, static on mobile)
- Ensure uniform typography scale across all content types
Print CSS overhaul:
- Fixed photo aspect ratio (60x80, 3:4 portrait) with contain fit
- Unified font sizes across all sections (10pt titles, 9pt content, 8pt metadata)
- Removed excessive spacing (reduced by 50-70%)
- Applied clean theme automatically (no sidebars, icons, logos, badges)
- Force short version for concise 5-page output
- Natural page breaks (removed forced breaks causing blank spaces)
- Consistent section title spacing with proper breathing room
- Match Training/Skills spacing pattern across all sections
- Fixed Languages and References spacing
- Equalized Experience, Courses, Projects, and Awards formatting
- Single separator for "See all projects" link
UI improvements:
- Enhanced icon toggle visibility with better contrast
- Reorganized navigation menu structure
- Created separate ui-en.json and ui-es.json files for UI strings
- Removed 'ui' section from cv-en.json and cv-es.json
- Added LoadUI() function to load UI translations separately
- Updated handlers to load UI data independently from CV data
- Updated template to use .UI instead of .CV.UI
This separation follows proper concerns:
- CV JSON files contain only professional CV content
- UI JSON files contain only application interface strings
- Each can be updated independently without affecting the other
- Added 'ui' section to cv-en.json and cv-es.json with modal translations
- Updated Go models to include UI, InfoModal, and TechStack structs
- Modified template to use JSON data instead of inline if/else statements
- Used template.HTML for Description field to allow HTML rendering
- Modal now fully supports English and Spanish translations
Translations include:
- Modal title
- Description text
- Tech stack labels (HTML5 Semántico vs Semantic HTML5, etc.)
- View source button text
Changes:
- Removed third line subtitle 'Built by myself'
- Added 'by myself' directly into the description text
- English: 'was built by myself with'
- Spanish: 'fue construido por mí mismo con'
- Increased spacing between title and CV title (1rem → 1.5rem)
- Removed unused .info-modal-subtitle CSS
- Cleaner, more natural text flow
Changes:
- Switched order: 'About this CV' now first, then CV title
- Changed format to 'CV 2025 - {JAMR}' (braces only around JAMR)
- Added subtitle 'Built by myself' / 'Construido por mí mismo'
- Adjusted spacing and styling for new hierarchy
- Subtitle is italic, gray, and smaller font size
- More personal and authentic presentation
Changes per user request:
- Replaced bouncing code icon with 'CV {YEAR} - JAMR' text (dynamic year)
- Made 'About this CV' title dark (#333) instead of green gradient
- Removed floating animation (@keyframes float)
- CV title styled in green with bold weight and letter spacing
- Cleaner, more professional header appearance
- Hamburger menu now opens on hover instead of click
- Menu stays open while hovering over button OR menu
- Added 100ms delay to prevent accidental closes
- Submenu opens on pure CSS :hover (no JavaScript)
- Smooth transitions for both main menu and submenu
- Proper ARIA attributes for accessibility
- Legacy click functions kept for backward compatibility
- Menu closes when mouse leaves both button and menu area
- Created semantic class 'menu-item-action' for action controls
- Applied width: calc(100% - 65px) to make items slightly narrower
- Applied text-align: center to center content
- Affects only Expand All and Collapse All menu items
- Preserves all existing functionality and base menu-item styles
- Makes action items visually distinct from navigation items
Features:
- Info button fixed on bottom-left (matches back-to-top on bottom-right)
- Modern glassmorphism modal with backdrop blur
- Smooth animations: scale + bounce effect on open
- Floating code icon animation
- Gradient text for title
- Tech stack showcase with hover effects (Go, HTMX, HTML5, CSS3)
- Beautiful GitHub link button with gradient and hover effects
- Multiple close methods: X button, click backdrop, Escape key
- Fully responsive for mobile devices
- No external libraries - pure CSS3 magic
Design highlights:
- Backdrop blur for depth
- White glass-effect card with subtle gradient
- Green accent color (#27ae60) throughout
- Smooth cubic-bezier transitions
- Rotating close button on hover
- Tech items lift on hover with shadows
- GitHub button with gradient that reverses on hover
Showcases modern web development skills with cutting-edge CSS
Changes:
- Save selected language to localStorage when user switches languages
- On page load with clean URL: check localStorage and load saved language preference
- If no localStorage preference exists, default to server's language choice
- Update language button active states based on localStorage preference
- Keep URLs clean (no lang parameter added) while maintaining language choice
- If URL has lang parameter, save it to localStorage for consistency
How it works:
1. Visit / (clean) → Shows English (default)
2. Switch to Spanish → Saves 'es' to localStorage, loads Spanish content
3. Click logo (clean URL /) → Page loads, checks localStorage, loads Spanish
4. Language persists across clean URL navigations via localStorage
This solves: Clean URLs + Language persistence