docs: add HTMX loading indicators as technique #10

Complete documentation of HTMX loading indicators implementation:

- Added comprehensive section matching style of other 9 techniques
- Documented external indicator pattern to avoid swap-target destruction
- Included root cause analysis of initial bug (indicators destroyed mid-animation)
- Detailed CSS implementation with GPU-accelerated animations
- Implementation locations: language selector, toggle controls, hamburger menu
- Benefits: zero JavaScript, automatic lifecycle, accessibility support
- Testing: automated tests in 4-htmx.test.mjs

Updated:
- Technique count: 8 → 10 major optimizations
- Final stats: now lists all 10 techniques
- Last updated date: 2025-11-18
This commit is contained in:
juanatsap
2025-11-18 16:24:28 +00:00
parent 52e85a5359
commit 54e2684127
+222 -4
View File
@@ -33,7 +33,7 @@ We achieve rich, interactive experiences with minimal JavaScript footprint.
--- ---
## 🏗️ Techniques Implemented (8 Major Optimizations) ## 🏗️ Techniques Implemented (10 Major Optimizations)
### 1. Native `<dialog>` Element - Modal Management ### 1. Native `<dialog>` Element - Modal Management
@@ -1393,6 +1393,214 @@ end
--- ---
## 🔄 HTMX Loading Indicators - Visual Feedback Pattern (COMPLETED)
### 10. HTMX Loading Indicators - External Indicator Pattern
**Problem:** HTMX requests happen asynchronously, but users had no visual feedback during operations like language switching or toggle state changes. Users would click and wonder if anything was happening, especially on slower connections.
**Original Challenge:** Initial implementation with indicators inside swap targets failed because indicators were destroyed mid-animation when parent elements were replaced by HTMX swaps.
**Solution:** Move indicators outside swap targets using `hx-indicator="#external-id"` pattern, ensuring they persist during DOM replacements.
#### Before (No Visual Feedback):
```html
<!-- User clicks, waits, sees nothing until swap completes -->
<button hx-get="/switch-language?lang=en"
hx-target="#language-selector"
hx-swap="outerHTML">
<span>English</span>
</button>
```
**User Experience:**
- ❌ Click → wait in silence → content appears
- ❌ No indication that request is processing
- ❌ Users click multiple times thinking it didn't work
- ❌ Poor perceived performance
#### After (Smooth Loading States):
```html
<!-- Wrapper keeps indicator outside swap target -->
<div class="language-selector-wrapper">
<!-- Indicators OUTSIDE swap target - persist during DOM replacement -->
<span id="lang-indicator-en" class="htmx-indicator small">
<iconify-icon icon="mdi:loading"
class="spinning light"
width="14"
height="14"
aria-label="Loading"></iconify-icon>
</span>
<span id="lang-indicator-es" class="htmx-indicator small">
<iconify-icon icon="mdi:loading"
class="spinning light"
width="14"
height="14"
aria-label="Loading"></iconify-icon>
</span>
<!-- Swap target - buttons get replaced, indicators persist -->
<div class="language-selector" id="language-selector">
<button hx-get="/switch-language?lang=en"
hx-target="#language-selector"
hx-swap="outerHTML"
hx-indicator="#lang-indicator-en">
<span>English</span>
</button>
<button hx-get="/switch-language?lang=es"
hx-target="#language-selector"
hx-swap="outerHTML"
hx-indicator="#lang-indicator-es">
<span>Español</span>
</button>
</div>
</div>
```
**User Experience:**
- ✅ Click → spinner appears immediately → content swaps → spinner disappears
- ✅ Clear visual feedback during entire request
- ✅ Professional, polished feel
- ✅ Users know their action was received
#### CSS Implementation:
```css
/* Base indicator styles - hidden by default */
.htmx-indicator {
opacity: 0;
transition: opacity 200ms ease-in-out;
pointer-events: none;
display: inline-flex;
align-items: center;
justify-content: center;
}
/* Show indicators during HTMX requests */
span.htmx-request.htmx-indicator,
.htmx-request .htmx-indicator,
.htmx-request.htmx-indicator {
opacity: 1 !important;
}
/* Spinning animation for loading icons */
.htmx-indicator.spinning {
animation: htmx-spin 1s linear infinite;
}
@keyframes htmx-spin {
from { transform: rotate(0deg); }
to { transform: rotate(360deg); }
}
/* Size variants */
.htmx-indicator.small {
width: 14px;
height: 14px;
}
.htmx-indicator.medium {
width: 18px;
height: 18px;
}
/* Color variants for different backgrounds */
.htmx-indicator.light {
color: rgba(255, 255, 255, 0.9);
}
.htmx-indicator.dark {
color: rgba(0, 0, 0, 0.7);
}
/* Respect reduced motion preference */
@media (prefers-reduced-motion: reduce) {
.htmx-indicator.spinning {
animation: none;
}
.htmx-indicator {
transition: none;
}
}
```
#### Toggle Controls Implementation:
```html
<!-- Length toggle with inline loading indicator -->
<div class="selector-group">
<label class="icon-toggle">
<input type="checkbox"
id="lengthToggle"
hx-post="/toggle/length"
hx-swap="none">
<span class="icon-toggle-slider">
<!-- Toggle icons -->
</span>
</label>
<!-- Indicator appears during save operation -->
<iconify-icon icon="mdi:loading"
class="htmx-indicator spinning small light"
width="14"
height="14"
aria-label="Saving"></iconify-icon>
</div>
```
**Benefits:**
-**Zero JavaScript required** - Pure HTMX + CSS pattern
-**Automatic lifecycle** - HTMX manages show/hide automatically
-**Smooth animations** - GPU-accelerated CSS transitions
-**No DOM destruction** - Indicators persist outside swap targets
-**Accessibility** - ARIA labels and reduced-motion support
-**Reusable pattern** - Same approach for all HTMX interactions
-**Better UX** - Immediate feedback improves perceived performance
-**Works everywhere** - Language buttons, toggles, forms
**Implementation Locations:**
1. **Language Selector** (`templates/partials/navigation/language-selector.html`)
- 2 external indicators for EN/ES buttons
- Spinning icon appears during language switch
2. **Toggle Controls** (`templates/partials/navigation/view-controls.html`)
- 3 inline indicators for Length/Icons/Theme toggles
- Subtle spinner shows during state save
3. **Hamburger Menu** (`templates/partials/navigation/hamburger-menu.html`)
- Same indicators as desktop version
- Consistent UX across breakpoints
**Root Cause Analysis - Initial Bug:**
**Timeline of Failure (Original Implementation):**
1. User clicks button
2. HTMX adds `.htmx-request` class to button
3. CSS starts opacity transition: `0 → 1` (target: 200ms)
4. **HTMX swap replaces parent element at ~7ms**
5. Indicator element **destroyed** mid-transition
6. Opacity reaches only `0.003` before destruction
7. New button rendered without `.htmx-request` class
8. **Result:** Indicator never visible to user
**Evidence from Playwright Monitoring:**
```
Time 585ms: htmx-request=true, opacity=0.000000
Time 592ms: htmx-request=false, opacity=0.003076 ← Transitioning but...
Time 600ms+: opacity=NaN ← Element destroyed!
```
**Fix:** Move indicators outside swap targets using wrapper pattern.
**Browser Support:** All modern browsers (99%+ global coverage)
**Testing:** Automated tests in `tests/mjs/4-htmx.test.mjs` verify:
- Indicators exist in DOM
- CSS classes applied correctly
- Smooth show/hide transitions
- No console errors during swap operations
**Key Innovation:** The external indicator pattern (`hx-indicator="#external-id"`) allows visual feedback to persist throughout entire HTMX request lifecycle, even when target elements are completely replaced.
---
## 🐛 Phase 9: Zoom Control Bug Fixes (November 2025) ## 🐛 Phase 9: Zoom Control Bug Fixes (November 2025)
### Issue 1: X Button Not Working ### Issue 1: X Button Not Working
@@ -1604,16 +1812,26 @@ set #shortcuts-button's *zoom to inverseZoom
--- ---
**Maintained by:** CV Project Development Team **Maintained by:** CV Project Development Team
**Last Updated:** 2025-11-16 **Last Updated:** 2025-11-18
**Status:** Phase 9 Complete ✅ | Zoom Control Fully Functional 🎉 **Status:** Phase 9 Complete ✅ | Zoom Control Fully Functional 🎉
**Final Stats:** **Final Stats:**
- 954 → 269 lines JavaScript (-71.8%) [+30 for zoom button reliability] - 954 → 269 lines JavaScript (-71.8%) [+30 for zoom button reliability]
- 9 major optimization techniques implemented - **10 major optimization techniques implemented**
1. Native `<dialog>` modals
2. CSS animations for lifecycle management
3. Native anchor links with smooth scrolling
4. HTMX scroll preservation
5. Native `<details>` accordions
6. CSS-first progressive menu system
7. Hyperscript declarative event handling
8. Hyperscript functions organization
9. Client-first toggles with `hx-swap="none"`
10. **HTMX loading indicators with external pattern**
- 165 lines organized hyperscript functions (scroll/print) + ~100 lines inline (toggles) - 165 lines organized hyperscript functions (scroll/print) + ~100 lines inline (toggles)
- Smooth "analogical" animations working perfectly - Smooth "analogical" animations working perfectly
- Zero HTMX swap errors (bug-free double-click) - Zero HTMX swap errors (bug-free double-click)
- All features preserved + improved UX - All features preserved + improved UX with professional loading states
- **Phase 9:** All zoom control bugs fixed with automated tests ✅ - **Phase 9:** All zoom control bugs fixed with automated tests ✅
--- ---