docs: Update skeleton loader implementation from hyperscript to JavaScript

MIGRATION SUMMARY:
- Moved skeleton loader logic from hyperscript to JavaScript (main.js)
- Changed from htmx:oobAfterSwap to htmx:afterSettle event
- Changed OOB swap from innerHTML to outerHTML for proper element replacement
- Added languageSwitching flag for state tracking
- Added 100ms delay after afterSettle for final render completion

DOCUMENTATION UPDATES:
- 2-MODERN-WEB-TECHNIQUES.md: Updated skeleton loader section with
This commit is contained in:
juanatsap
2025-11-18 19:32:28 +00:00
parent 65eb91b00b
commit 1f6f8e417e
13 changed files with 539 additions and 150 deletions
+62 -28
View File
@@ -1930,31 +1930,57 @@ Time 600ms+: opacity=NaN ← Element destroyed!
```
```javascript
// Language switch with skeleton loading
htmx.on('htmx:beforeSwap', function(evt) {
// Show skeletons BEFORE swap
document.querySelectorAll('.component-wrapper').forEach(wrapper => {
wrapper.classList.add('loading');
});
// static/js/main.js - Skeleton loader for language transitions
let languageSwitching = false;
// Add .loading class when language button is clicked
document.addEventListener('htmx:beforeRequest', function(evt) {
const element = evt.detail.elt;
if (element && element.classList && element.classList.contains('selector-btn')) {
// Set flag to track language switching
languageSwitching = true;
// Add loading class to page containers
const page1 = document.getElementById('cv-inner-content-page-1');
const page2 = document.getElementById('cv-inner-content-page-2');
if (page1) page1.classList.add('loading');
if (page2) page2.classList.add('loading');
}
});
htmx.on('htmx:afterSettle', function(evt) {
// Hide skeletons AFTER content settles
document.querySelectorAll('.component-wrapper').forEach(wrapper => {
wrapper.classList.remove('loading');
});
// Remove .loading class after language transition completes
document.addEventListener('htmx:afterSettle', function(evt) {
if (languageSwitching) {
// Wait for final render to complete
setTimeout(function() {
const page1 = document.getElementById('cv-inner-content-page-1');
const page2 = document.getElementById('cv-inner-content-page-2');
if (page1) page1.classList.remove('loading');
if (page2) page2.classList.remove('loading');
// Reset flag
languageSwitching = false;
}, 100);
}
});
```
**Architecture Pattern:**
1. **User clicks language toggle** → HTMX `beforeSwap` event fires
2. **JavaScript adds `.loading` class**Triggers CSS transition (actual-content opacity: 1 → 0, skeleton-content opacity: 0 → 1)
3. **Skeleton appears** → Smooth 250ms fade-in with shimmer animation
4. **HTMX fetches new language content** → Server renders and returns HTML
5. **HTMX swaps content** → New actual-content replaces old
6. **afterSettle event fires** → JavaScript removes `.loading` class
7. **Skeleton fades out**Smooth 250ms fade (skeleton opacity: 1 → 0, actual-content opacity: 0 → 1)
8. **Result** → Smooth, professional loading experience with zero layout shift
1. **User clicks language button** → HTMX `htmx:beforeRequest` event fires
2. **JavaScript detects `.selector-btn` click**Sets `languageSwitching` flag
3. **JavaScript adds `.loading` to parent containers** → Triggers CSS cascade to child `.component-wrapper` elements
4. **Skeleton appears** → CSS transition (actual-content opacity: 1 → 0, skeleton-content opacity: 0 → 1) + shimmer animation
5. **HTMX fetches new language content** → Server renders and returns HTML via OOB swaps
6. **HTMX swaps content** → Out-of-band (OOB) swap replaces page containers
7. **`htmx:afterSettle` event fires** → JavaScript waits 100ms for final render
8. **JavaScript removes `.loading` class** → CSS transition reverses (skeleton opacity: 1 → 0, actual-content opacity: 0 → 1)
9. **Result** → Smooth, professional loading experience with zero layout shift
**Why JavaScript Instead of Hyperscript:**
-**Reliable Playwright testing** - JavaScript event handlers work consistently in automated tests
-**Debugging** - Console.log statements provide clear execution tracking
-**Maintainability** - Standard JavaScript patterns familiar to all developers
-**Performance** - Direct DOM manipulation, no hyperscript parser overhead
**Benefits:**
-**Zero layout shift** - Skeletons match exact dimensions of actual content
@@ -1966,16 +1992,24 @@ htmx.on('htmx:afterSettle', function(evt) {
-**Reusable** - Works for any HTMX swap operation, not just language switch
**Implementation Locations:**
- **CSS:** `static/css/skeleton.css` (341 lines) - Complete skeleton system
- **Template:** `templates/partials/skeleton-loader.html` - Skeleton placeholders
- **Component wrappers:** Each CV section wrapped with `.component-wrapper`
- **HTMX events:** `static/js/main.js` - beforeSwap/afterSettle listeners
- **CSS:** `static/css/skeleton.css` - Complete skeleton system with shimmer animations
- **JavaScript:** `static/js/main.js` (lines 231-273) - HTMX event handlers for skeleton control
- **Templates:** `templates/partials/sections/header.html` - Component wrapper structure
- **Page Containers:** `templates/cv-content.html` - Parent containers receiving `.loading` class
- **Language Switch:** `templates/language-switch.html` - `.selector-btn` triggers skeleton display
**Testing:** Automated tests in `tests/mjs/12-skeleton-language-transitions.test.mjs` verify:
- Skeletons appear during language switch
- Content replaced without full page reload
- Skeletons removed after content loads
- Zero layout shift during transition
- ✅ Component wrapper structure (dual-state: actual + skeleton content)
- ✅ Skeleton CSS loaded (shimmer animation verified)
- ✅ First language switch (EN → ES) - Loading class added/removed
- ✅ Second language switch (ES → EN) - Consistent behavior
- ✅ Third language switch (EN → ES) - Regression check
- ✅ No stuck loading states (all containers clean after transition)
- ✅ JavaScript event handlers configured (languageSwitching flag)
**Test Results:** 7/7 tests pass - Complete validation of skeleton loader functionality
**Run Test:** `bun tests/mjs/12-skeleton-language-transitions.test.mjs`
**Pixel-Perfect Matching:**
@@ -2354,7 +2388,7 @@ set #shortcuts-button's *zoom to inverseZoom
3. `templates/partials/navigation/hamburger-menu.html`
- Removed conflicting hyperscript from show zoom button
4. `MODERN-WEB-TECHNIQUES.md`
4. `2-MODERN-WEB-TECHNIQUES.md`
- Updated documentation to reflect fixes
- Added technical lessons learned