60c1b5ac2b
- Add 6 new utility functions (closeOnBackdrop, scrollToTop, etc.) - Add 10 zoom functions including drag handlers - Document hyperscript reserved words (target, me, it, event) - Add example showing the 'target' parameter pitfall - Update file organization descriptions - Add Phase 2 refactoring details to recent changes
293 lines
11 KiB
Markdown
293 lines
11 KiB
Markdown
# 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:
|
|
```html
|
|
<input type="checkbox"
|
|
id="lengthToggle"
|
|
_="on change call toggleCVLength(my.checked)">
|
|
```
|
|
|
|
❌ **BAD** - Inline logic nightmare:
|
|
```html
|
|
<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:**
|
|
```html
|
|
<!-- 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:**
|
|
```hyperscript
|
|
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:**
|
|
```html
|
|
<!-- 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:**
|
|
|
|
```hyperscript
|
|
-- 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:**
|
|
```html
|
|
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, modals, expand/collapse)
|
|
├── 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 (slider, reset, drag handlers, visibility)
|
|
└── pdf-modal._hs → PDF modal helpers (selectPdfCard, handlePdfCardKey)
|
|
```
|
|
|
|
### Load Order in templates/index.html:
|
|
```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
|
|
4. `closeOnBackdrop(modal, evt)` - Close modal when clicking backdrop (outside content)
|
|
5. `scrollToTop(evt)` - Smooth scroll to top of page
|
|
6. `scrollToSection(evt, sectionId)` - Smooth scroll to section with menu close
|
|
7. `expandAllSections(evt)` - Expand all `<details>` elements
|
|
8. `collapseAllSections(evt)` - Collapse all `<details>` elements
|
|
9. `setFooterHover(show)` - Add/remove footer-hovered class on fixed buttons
|
|
|
|
### 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
|
|
|
|
### Zoom Functions (zoom._hs)
|
|
1. `handleZoomInput(slider)` - Handle zoom slider input changes
|
|
2. `handleZoomReset()` - Reset zoom to 100%
|
|
3. `initZoomControl(control)` - Initialize zoom control on page load
|
|
4. `showZoomControl()` - Show the zoom control panel
|
|
5. `hideZoomControl()` - Hide the zoom control panel
|
|
6. `toggleZoomControl()` - Toggle zoom control visibility
|
|
7. `isZoomDragTarget(el)` - Check if element is valid drag target (not button/input)
|
|
8. `startZoomDrag(control, clientX, clientY)` - Start dragging zoom control
|
|
9. `moveZoomDrag(control, clientX, clientY)` - Handle drag movement
|
|
10. `endZoomDrag(control)` - End drag and save position
|
|
|
|
### Navigation Functions (moved to utils._hs)
|
|
*Note: `scrollToSection` moved to utils._hs for consolidation*
|
|
|
|
## 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)
|
|
❌ **DON'T**: Use `target` as a parameter name - it's a reserved word!
|
|
|
|
✅ **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
|
|
✅ **DO**: Use `el` instead of `target` when passing DOM elements to functions
|
|
|
|
### Reserved Words in Hyperscript
|
|
The following are reserved and reference special values in hyperscript:
|
|
- `target` → `event.target` (the element that triggered the event)
|
|
- `me` → The current element with the `_=""` attribute
|
|
- `it` → The result of the previous command
|
|
- `event` → The current event object
|
|
|
|
**Example of the `target` pitfall:**
|
|
```hyperscript
|
|
-- ❌ WRONG - 'target' is reserved, will reference event.target
|
|
def checkElement(target)
|
|
if target.tagName is 'INPUT' -- ERROR: target is null in function context
|
|
return false
|
|
end
|
|
end
|
|
|
|
-- ✅ CORRECT - use 'el' instead
|
|
def checkElement(el)
|
|
if el.tagName is 'INPUT'
|
|
return false
|
|
end
|
|
end
|
|
```
|
|
|
|
## 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 (Phase 2)
|
|
- ✅ **REFACTORED**: Modal backdrop close (3 modals) → `closeOnBackdrop()` in `utils._hs`
|
|
- ✅ **REFACTORED**: Back-to-top button → `scrollToTop()` in `utils._hs`
|
|
- ✅ **REFACTORED**: Zoom drag handlers (~35 lines) → 4 functions in `zoom._hs`
|
|
- ✅ **ADDED**: `isZoomDragTarget()`, `startZoomDrag()`, `moveZoomDrag()`, `endZoomDrag()`
|
|
- ✅ **ADDED**: `showZoomControl()`, `hideZoomControl()`, `toggleZoomControl()`
|
|
- ✅ **MOVED**: `expandAllSections()`, `collapseAllSections()` to `utils._hs`
|
|
- ✅ **MOVED**: `scrollToSection()` to `utils._hs` with integrated menu close
|
|
- ✅ **LEARNING**: `target` is a reserved word in hyperscript (use `el` instead)
|
|
- ✅ **TESTED**: All 21 functions verified, 6 functional tests passed
|
|
|
|
### 2025-11-30: Major Inline Hyperscript Refactoring (Phase 1)
|
|
- ✅ **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
|