Files
cv-site/doc/4-HYPERSCRIPT-RULES.md
T
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

8.8 KiB

Hyperscript Development Rules

MANDATORY RULES - ALWAYS FOLLOW

Rule 1: Code Cleanliness

More than 3 lines of hyperscript → Move to function in file

  • Inline hyperscript in HTML should be kept minimal (≤3 lines)
  • Longer logic MUST be extracted to named functions in ._hs files
  • HTML templates should be clean and readable

Rule 2: File Structure - Organized by Category

NO def statement limit with latest hyperscript!

HISTORICAL NOTE (2025-11-17): Hyperscript 0.9.12 had a 3 def limit. Latest version REMOVED this limitation.

  • Test verification: tests/mjs/9-hyperscript-def-limit.test.mjs (all 5 def statements passed)
  • Migration in progress: Moving functions from JavaScript back to hyperscript

Current Best Practice: Organize hyperscript functions by category in separate files

static/hyperscript/
├── toggles._hs        # Toggle functions (CV length, icons, theme)
├── hover-sync._hs     # Hover synchronization functions
├── utils._hs          # Utility functions (print, scroll, etc.)
└── keyboard._hs       # Keyboard shortcut handlers

Benefits of Hyperscript Organization:

  • Clean separation of concerns
  • Easy to locate and maintain functions
  • No artificial limitations
  • Server-side hypermedia pattern (functions loaded with HTML)
  • Still callable from anywhere using call functionName(args)

Rule 3: HTML Structure Cleanliness

HTML must be as clean as possible regarding hyperscript

GOOD - Clean, readable:

<input type="checkbox"
       id="lengthToggle"
       _="on change call toggleCVLength(my.checked)">

BAD - Inline logic nightmare:

<input type="checkbox"
       id="lengthToggle"
       _="on change
            if my.checked
              remove .cv-short from .cv-paper
              add .cv-long to .cv-paper
              set localStorage['cv-length'] to 'long'
              set #lengthToggleMenu's checked to true
            else
              remove .cv-long from .cv-paper
              add .cv-short to .cv-paper
              set localStorage['cv-length'] to 'short'
              set #lengthToggleMenu's checked to false
            end">

Rule 4: Event Handler Externalization Guidelines (2025-11-20)

Know what CAN and CANNOT be externalized

CAN Be Externalized:

Simple function calls without complex event inspection:

<!-- Navigation handlers -->
<a href="#section" _="on click call scrollToSection(event, 'section')">

<!-- Toggle handlers -->
<input _="on change call toggleCVLength(my.checked)">

<!-- Hover handlers -->
<button _="on mouseenter call syncPdfHover(true)
           on mouseleave call syncPdfHover(false)">

External function example:

def scrollToSection(event, sectionId)
  call event.preventDefault()
  set element to document.getElementById(sectionId)
  if element then call element.scrollIntoView({behavior: 'smooth'}) end
end

MUST Stay Inline:

Complex event handlers that inspect event properties:

<!-- Keyboard handlers with event.key, event.target inspection -->
<body _="on keydown
          set tagName to event.target.tagName
          set isInputField to (tagName is 'INPUT' or tagName is 'TEXTAREA')
          if event.key is 'l' and not event.ctrlKey and not isInputField
            halt the event
            -- handler logic here
          end
        end">

Why: The event variable in _="" attributes is a hyperscript runtime variable. External def functions don't have direct access to this event context, causing scoping issues.

🎯 Optimization for Inline Handlers:

Use then chains to make inline code more compact:

-- Before (verbose)
if condition
  do step1
  do step2
  do step3
end

-- After (compact with then chains)
if condition
  do step1 then do step2 then do step3
end

Example from body keyboard handler:

if event.key is '?' and not event.ctrlKey and not event.metaKey and not event.altKey and not isInputField
  halt the event then set modal to #shortcuts-modal then if modal then call modal.showModal() end
end

File Organization

static/hyperscript/
├── utils._hs         → Core utilities (scroll, print, etc.)
├── toggles._hs       → Toggle functions (CV length, icons, theme)
├── hover-sync._hs    → Hover sync functions (PDF, print, zoom)
├── keyboard._hs      → Keyboard shortcut helpers (handleToggleShortcut, openModalShortcut)
├── zoom._hs          → Zoom control helpers (handleZoomInput, handleZoomReset, initZoomControl)
└── pdf-modal._hs     → PDF modal helpers (selectPdfCard, handlePdfCardKey)

Load Order in templates/index.html:

<script type="text/hyperscript" src="/static/hyperscript/utils._hs"></script>
<script type="text/hyperscript" src="/static/hyperscript/toggles._hs"></script>
<script type="text/hyperscript" src="/static/hyperscript/hover-sync._hs"></script>
<script type="text/hyperscript" src="/static/hyperscript/keyboard._hs"></script>
<script type="text/hyperscript" src="/static/hyperscript/zoom._hs"></script>
<script type="text/hyperscript" src="/static/hyperscript/pdf-modal._hs"></script>
<script src="https://unpkg.com/hyperscript.org@0.9.14"></script>

Required Functions

Core Functions (utils._hs)

  1. printFriendly() - Handle print-friendly view
  2. initScrollBehavior() - Initialize scroll variables
  3. handleScroll() - Manage scroll behavior and fixed button positioning

Toggle Functions (toggles._hs)

  1. toggleCVLength(isLong) - Switch between short/long CV
  2. toggleIcons(showIcons) - Show/hide icons
  3. toggleTheme(isClean) - Switch between default/clean theme

Hover Sync Functions (hover-sync._hs)

  1. syncPdfHover(show) - Sync hover state across PDF buttons
  2. syncPrintHover(show) - Sync hover state across print buttons
  3. highlightZoomControl(show) - Highlight zoom control on hover

Navigation Functions (navigation._hs) [2025-11-20]

  1. scrollToSection(event, sectionId) - Smooth scroll to CV section

Why These Rules Exist

Maintainability

  • Functions with descriptive names are self-documenting
  • Easier to test and debug
  • Changes in one place instead of scattered across templates

Performance

  • Browser caches ._hs files
  • Reduces HTML payload size
  • Cleaner separation of concerns

Historical Note: Hyperscript Def Limit

  • Hyperscript 0.9.12 had a 3-def limit per file (FIXED in 0.9.14+)
  • Hyperscript 0.9.14+ has NO def limit - tested with 5+ defs
  • Multi-file organization is still recommended for maintainability, not required

Common Mistakes to Avoid

DON'T: Write long inline hyperscript in HTML (maintainability issue) DON'T: Try to externalize event handlers that inspect event.key or event.target DON'T: Forget to test after refactoring (syntax errors look like bugs)

DO: Split functions across multiple ._hs files for organization DO: Keep HTML clean with function calls DO: Test all keyboard shortcuts after any hyperscript changes

Testing After Changes

  1. Check browser console for parse errors
  2. Verify all functions are defined (no "X is null" errors)
  3. Test all toggles work correctly
  4. Hard refresh browser (Ctrl+Shift+R) to clear cache

Recent Changes

2025-11-30: Major Inline Hyperscript Refactoring

  • REFACTORED: Body tag keyboard handlers → keyboard._hs helper functions
  • REFACTORED: Zoom control handlers → zoom._hs helper functions
  • REFACTORED: PDF modal card selection (3 identical blocks) → pdf-modal._hs
  • ADDED: zoom._hs - Zoom control helpers (handleZoomInput, handleZoomReset, initZoomControl)
  • ADDED: pdf-modal._hs - PDF modal helpers (selectPdfCard, handlePdfCardKey)
  • TESTED: All functionality verified with comprehensive tests

2025-11-30: Multi-File Loading Bug Investigation

  • CONFIRMED: Multiple <script type="text/hyperscript" src="..."> tags work correctly
  • VERIFIED: No multi-file loading bug in hyperscript 0.9.14
  • TESTED: All 6 external files + inline hyperscript work together seamlessly
  • ADDED: Test tests/mjs/32-hyperscript-multi-src.test.mjs for verification
  • 🔍 FINDING: Previous refactoring failures were syntax errors, NOT hyperscript bugs

2025-11-20: Event Handler Externalization Guidelines

  • Added Rule 4: Clear guidelines on what can/cannot be externalized
  • Navigation handlers successfully externalized (9 links → 1 function)
  • Documented then chain optimization for inline handlers
  • Updated file organization with navigation._hs
  • ⚠️ Keyboard handlers documented to stay inline (event context requirement)

Last Updated: 2025-11-30 Hyperscript Version: 0.9.14 Status: MANDATORY - ALWAYS FOLLOW