- Download htmx.min.js v2.0.10 and _hyperscript.min.js v0.9.91 locally
- Update head-scripts.html to load from /static/ instead of unpkg CDN
- Remove https://unpkg.com from CSP script-src whitelist
- Update all documentation references to reflect self-hosted paths
- No breaking changes: all hx-* attributes are HTMX 2.0 compatible
- Use event filtering [key is 'Enter' or key is ' '] on PDF modal cards
- Remove handlePdfCardKey helper function (now inline)
- Use event destructuring on keydown(key, target, ctrlKey, metaKey, altKey)
- Cleaner, more idiomatic hyperscript patterns
Replace verbose document.getElementById() and document.querySelectorAll()
with cleaner hyperscript syntax:
- #id for ID selectors
- .class and the first .class for class selectors
- <selector/> query literals for complex selectors
- #{variable} for dynamic ID interpolation
Files changed:
- utils._hs: scrollHeight, details, footer buttons, scrollToSection
- zoom._hs: all zoom control element selectors (14 changes)
- pdf-modal._hs: modal selector
- keyboard._hs: dynamic toggle and modal selectors
- contact-modal.html: response div and modal close
- index.html: ninja-keys bar selector
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.
- 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
- 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.
- 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.
- 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.
- 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)
**Bug 1: Wrong at-bottom colors**
- All buttons were showing orange (#f39c12) when at bottom
- User wanted each button to use its own hover color
**Bug 2: Zoom button missing at-bottom state**
- Zoom/search button wasn't changing color at bottom
**Fix - Updated .at-bottom styles to match hover colors:**
Download PDF button:
- Was: Orange (#f39c12)
- Now: PDF Red (#cd6060) - matches hover
Print Friendly button:
- Was: Orange (#f39c12)
- Now: White background + Green icon (#27ae60) - matches hover
Shortcuts button:
- Kept: Orange (#f39c12) - already correct
Color Theme Switcher:
- Was: Uniform orange
- Now: Dynamic colors based on theme mode
- Light mode: Yellow (#f39c12)
- Dark mode: Blue (#3498db)
- Auto mode: Purple (#9b59b6)
Info button:
- Already correct: Green (#27ae60)
Back-to-top button:
- Already correct: Green (#27ae60)
Zoom button:
- Added: Blue (#3498db) - matches hover
- Added to scroll handler in utils._hs
**Result**: Each button now shows its characteristic color when page
is scrolled to bottom, providing consistent visual feedback that matches
the hover state.
Files changed:
- static/hyperscript/utils._hs: Added zoom button
- static/css/main.css: Updated 3 button colors
- static/css/color-theme.css: Dynamic theme colors
**Issue**: Only info and shortcuts buttons were showing orange color when
scrolled to bottom of page. Download PDF, Print Friendly, and Color Theme
buttons were missing this visual feedback.
**Fix**:
1. Added all buttons to at-bottom class logic in utils._hs:
- #download-button
- #print-friendly-button
- .color-theme-switcher
2. Added CSS .at-bottom styles for missing buttons:
- .download-btn.at-bottom
- .print-friendly-btn.at-bottom
- .color-theme-switcher.at-bottom
**Result**: All fixed buttons now show unified orange (#f39c12) background
when page is scrolled to bottom (within 50px), providing consistent visual
feedback across all controls.
Files changed:
- static/hyperscript/utils._hs: Added 3 buttons to scroll handler
- static/css/main.css: Added 2 .at-bottom styles
- static/css/color-theme.css: Added 1 .at-bottom style
**Bug 1: Bidirectional Hover Sync**
- Fixed hover sync to work in BOTH directions
- Hovering left buttons → highlights menu buttons (was working)
- Hovering menu buttons → highlights left buttons (NOW WORKING)
- Added .download-btn and .print-friendly-btn to syncPdfHover/syncPrintHover selectors
**Bug 2: Theme-Aware CV Text Colors**
- CV content text now adapts to light/dark themes
- Light theme: Dark text (#1a1a1a, #333333) for readability
- Dark theme: Light text (#e0e0e0, #d0d0d0) for readability
- Added theme-specific overrides for --text-dark and --text-gray variables
- Solution uses CSS variables (not hardcoded) for future flexibility
Changes:
- static/hyperscript/hover-sync._hs: Added left-side button selectors
- static/css/color-theme.css: Added legacy variable overrides for all themes
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
CRITICAL FIX: Icon toggle now works without page refresh
- Changed class name from 'show-logos' to 'show-icons' (CSS mismatch bug)
- Updated localStorage key from 'cv-logos' to 'cv-icons'
- Fixed toggleIcons() function in cv-functions.js
HYPERSCRIPT ARCHITECTURE:
- Moved 6 toggle functions from hyperscript to JavaScript (cv-functions.js)
- Solves hyperscript 0.9.14 parser limitation (max 3 def statements total)
- Upgraded hyperscript from 0.9.12 to 0.9.14
- Fixed operator precedence in keyboard shortcuts
- Cleaned view-controls.html templates (inline → function calls)
NEW FILES:
- static/js/cv-functions.js - Global toggle functions (6 functions)
- HYPERSCRIPT-RULES.md - Permanent architecture documentation
- tests/mjs/0-zoom.test.mjs - Zoom functionality test
- tests/mjs/1-toggles.test.mjs - Comprehensive toggle test with real-time verification
- tests/TEST-SUMMARY.md - Test suite documentation
TESTS:
- Real-time DOM update verification (no refresh required)
- Screenshot capture for visual regression
- localStorage persistence validation
- Toggle synchronization between action bar and menu
BREAKING CHANGE: localStorage key changed from 'cv-logos' to 'cv-icons'
Users may need to re-toggle icons preference on first load after update.
**Problem**: Toggles in action bar and hamburger menu were not
synchronizing. When user clicked one toggle, the other didn't update.
**Root Cause**: After restoring toggle functions, they were setting BOTH
checkboxes explicitly instead of using the hyperscript 'or' operator
pattern from the working version.
**Fix**: Restored the original pattern from commit d4ef91b:
- Use `set otherToggle to (#lengthToggle or #lengthToggleMenu)`
- This dynamically selects "the other" toggle (not the one being clicked)
- Simplified code: removed redundant null checks for both checkboxes
- Fixed all 3 toggles: toggleCVLength, toggleIcons, toggleTheme
**Benefits**:
- Toggles now properly sync between action bar and menu
- Cleaner, more maintainable code
- Matches the proven working pattern
**Reference**: Compared with working version at commit d4ef91b
All three toggle functions (toggleCVLength, toggleIcons, toggleTheme) now
check if checkbox elements exist before setting their 'checked' property.
This prevents null pointer errors when:
- Menu checkboxes aren't rendered yet
- Functions are called programmatically before DOM is ready
- Tests call functions directly without full page render
Changed pattern from:
set checkbox's checked to true
To:
if checkbox exists
set checkbox's checked to true
end
Affected functions:
- toggleCVLength(): Lines 142-147, 154-159
- toggleIcons(): Lines 172-177, 183-188
- toggleTheme(): Lines 201-206, 212-217
The functions still work correctly when checkboxes exist, but now gracefully
handle missing elements instead of throwing null errors.
The CSS rules for showing/hiding icons target .cv-paper, not .cv-container.
Changed toggleIcons function to:
- Add/remove .show-icons class on .cv-paper (not .cv-container)
- This matches the CSS selectors in logo-toggle.css
Bug: Icons toggle wasn't working because class was being applied to wrong element
Fix: Apply .show-icons to .cv-paper to match CSS rules
Added 6 critical hyperscript functions that were lost during parse error fix:
- toggleCVLength(isLong) - Toggle between long/short CV views with localStorage
- toggleIcons(showIcons) - Toggle icon visibility with persistence
- toggleTheme(isClean) - Toggle between default/clean themes
- syncPdfHover(show) - Synchronized hover effects for PDF download buttons
- syncPrintHover(show) - Synchronized hover effects for print buttons
- highlightZoomControl(show) - Highlight zoom control on interaction
All functions use hyperscript 0.9.12 compatible syntax (no 'else' blocks).
Each toggle function syncs between action bar and menu checkboxes.
Verified:
- Zero parse errors in browser console
- All 9 hyperscript functions properly defined
- File grew from 134 to 250 lines (+87%)
- Compatible with existing HTML templates
Reverted static/hyperscript/functions._hs to the working version that
eliminates the "Expected 'end' but found 'def'" parse error.
The issue was caused by the expanded version with toggle functions.
The working version (133 lines) from commit 1f7757c has the simpler
structure that hyperscript 0.9.12 can parse correctly.
Verified with comprehensive browser testing:
- No parse errors in browser console
- Scroll behavior works correctly
- Back-to-top button shows/hides as expected
- All hyperscript functions load successfully
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