docs: refactor skeleton loader architecture from overlay to component-based

Changed skeleton loader implementation strategy from separate overlay to built-in component states:

ARCHITECTURE CHANGE:
- Before: Separate skeleton-loader.html overlay positioned absolutely over content
- After: Each component contains both actual content and skeleton state markup
- Skeleton states toggle via CSS classes (.loading) on component wrappers
- No separate overlay layer - skeleton lives inside each component

IMPLEMENTATION
This commit is contained in:
juanatsap
2025-11-17 13:50:45 +00:00
parent ad6f72f92c
commit 836e1e228d
+83 -59
View File
@@ -32,7 +32,7 @@ This will transform the current instant language switch into a premium, modern w
**Desired User Experience (from screenshots):** **Desired User Experience (from screenshots):**
1. User clicks language button (EN or ES) 1. User clicks language button (EN or ES)
2. Current content fades out (200-300ms) 2. Current content fades out (200-300ms)
3. Skeleton loaders appear - gray pulsing boxes matching the layout structure 3. Skeleton loaders appear - gray pulsing boxes matching INSIDE of the CV (so, a toggle of the content)
4. New language content loads from server 4. New language content loads from server
5. Skeleton loaders fade out and new content fades in (200-300ms) 5. Skeleton loaders fade out and new content fades in (200-300ms)
6. Total smooth, professional transition feel 6. Total smooth, professional transition feel
@@ -98,15 +98,22 @@ This will transform the current instant language switch into a premium, modern w
<implementation> <implementation>
**Recommended Implementation Strategy:** **Recommended Implementation Strategy:**
**Step 1: Create Skeleton Loader HTML Structure** **Step 1: Add Skeleton States to Existing Components**
Create a new template or partial: `templates/partials/skeleton-loader.html` Modify existing CV component templates to include built-in skeleton states:
This should contain a simplified version of your CV layout with placeholder boxes: - `templates/partials/cv-header.html` - Add skeleton state markup inside the component
- Header skeleton (circular avatar placeholder, horizontal bars for name/title) - `templates/partials/skills-*.html` - Add skeleton placeholders for skill blocks
- Left sidebar skeleton (rectangular blocks for skills) - `templates/partials/experience-entry.html` - Add skeleton placeholders for experience blocks
- Main content skeleton (blocks for experience entries) - Each component should have both actual content and skeleton markup, toggled via CSS classes
- Right sidebar skeleton (blocks for additional skills)
Example structure:
```html
<div class="component-wrapper">
<div class="actual-content"><!-- Real CV content --></div>
<div class="skeleton-content hidden"><!-- Skeleton placeholders --></div>
</div>
```
**Step 2: Create Skeleton CSS Animations** **Step 2: Create Skeleton CSS Animations**
@@ -126,21 +133,29 @@ Add to `static/css/main.css` or create `static/css/skeleton.css`:
50% { background-position: 0 0; } 50% { background-position: 0 0; }
} }
/* Skeleton container - hidden by default */ /* Component state toggling */
.skeleton-overlay { .component-wrapper .actual-content {
opacity: 1;
transition: opacity 250ms ease;
}
.component-wrapper .skeleton-content {
opacity: 0;
position: absolute; position: absolute;
top: 0; top: 0;
left: 0; left: 0;
right: 0; right: 0;
bottom: 0;
background: white;
z-index: 10;
opacity: 0;
pointer-events: none; pointer-events: none;
transition: opacity 250ms ease; transition: opacity 250ms ease;
} }
.skeleton-overlay.active { /* Loading state - toggle content visibility */
.component-wrapper.loading .actual-content {
opacity: 0;
pointer-events: none;
}
.component-wrapper.loading .skeleton-content {
opacity: 1; opacity: 1;
pointer-events: all; pointer-events: all;
} }
@@ -160,36 +175,39 @@ Add to `static/css/main.css` or create `static/css/skeleton.css`:
Modify `templates/partials/navigation/language-selector.html`: Modify `templates/partials/navigation/language-selector.html`:
- Add `hx-swap="outerHTML swap:250ms settle:250ms"` - Add `hx-swap="outerHTML swap:250ms settle:250ms"`
- Add `hx-indicator="#skeleton-loader"` to show skeleton during request - Add hyperscript to toggle `.loading` class on component wrappers
- Add hyperscript to manage skeleton visibility - Coordinate timing with component fade-in/out transitions
**Step 4: Add Skeleton Loader to Main Template** **Step 4: Ensure Component Wrappers Have Proper Classes**
Insert the skeleton loader overlay into your main CV template: Verify that CV component wrappers in templates have:
- Position it absolutely over the content area - `.component-wrapper` class for CSS targeting
- Initially hidden with `opacity: 0` - Both `.actual-content` and `.skeleton-content` children
- Activated via HTMX events or hyperscript - Proper positioning context (relative positioning on wrapper)
**Step 5: HTMX Event Handling with Hyperscript** **Step 5: HTMX Event Handling with Hyperscript**
Add hyperscript behavior to show/hide skeleton: Add hyperscript behavior to toggle loading state on components:
```hyperscript ```hyperscript
on htmx:beforeRequest from #language-selector on htmx:beforeRequest from #language-selector
add .active to #skeleton-loader add .loading to .component-wrapper in #cv-inner-content-page-1
add .loading to .component-wrapper in #cv-inner-content-page-2
end end
on htmx:afterSwap from #language-selector on htmx:afterSwap from #language-selector
wait 100ms -- Brief delay to ensure content is rendered wait 100ms -- Brief delay to ensure content is rendered
remove .active from #skeleton-loader remove .loading from .component-wrapper in #cv-inner-content-page-1
remove .loading from .component-wrapper in #cv-inner-content-page-2
end end
``` ```
**Alternative Approach (Pure HTMX):** **Alternative Approach (CSS-based):**
Use `hx-indicator` attribute with CSS to control visibility: Use HTMX's automatic class management:
- `hx-indicator="#skeleton-loader"` on language buttons - Add `.component-wrapper` to elements receiving OOB swaps
- HTMX automatically adds `.htmx-request` class during requests - Use HTMX classes: `.htmx-swapping` triggers skeleton state
- CSS: `#skeleton-loader.htmx-request { opacity: 1; }` - CSS: `.component-wrapper.htmx-swapping .actual-content { opacity: 0; }`
- CSS: `.component-wrapper.htmx-swapping .skeleton-content { opacity: 1; }`
**Step 6: Content Fade Transitions** **Step 6: Content Fade Transitions**
@@ -210,13 +228,15 @@ Add fade-in/out to actual content sections:
``` ```
**What to Prioritize:** **What to Prioritize:**
1. Get basic skeleton structure showing/hiding correctly 1. Add skeleton markup inside each existing component (dual-state structure)
2. Add pulsing animation 2. Implement CSS toggling between actual content and skeleton content
3. Refine skeleton shapes to better match layout 3. Add pulsing animation to skeleton elements
4. Polish timing and transitions 4. Refine skeleton shapes to better match layout
5. Add accessibility features 5. Polish timing and transitions
6. Add accessibility features
**What to Avoid:** **What to Avoid:**
- Don't create a separate overlay layer - skeleton states live inside components
- Don't create overly complex skeleton markup - simple boxes are fine - Don't create overly complex skeleton markup - simple boxes are fine
- Don't make the skeleton identical to real content - approximate is better - Don't make the skeleton identical to real content - approximate is better
- Don't use JavaScript animations - stick to CSS for performance - Don't use JavaScript animations - stick to CSS for performance
@@ -233,22 +253,23 @@ Add fade-in/out to actual content sections:
<output> <output>
Create/modify the following files: Create/modify the following files:
1. **`./templates/partials/skeleton-loader.html`** (NEW) 1. **Modify existing component templates** (UPDATE)
- Skeleton loader HTML structure - `./templates/partials/cv-header.html` - Add skeleton state inside component
- Simplified CV layout with placeholder boxes - `./templates/partials/skills-*.html` - Add skeleton placeholders
- Should mirror the structure of CV content sections - `./templates/partials/experience-entry.html` - Add skeleton placeholders
- Each component gets dual-state structure (actual content + skeleton content)
2. **`./static/css/skeleton.css`** (NEW) 2. **`./static/css/skeleton.css`** (NEW)
- Skeleton loader styles - Skeleton loader styles
- Pulsing animation keyframes - Pulsing animation keyframes
- Skeleton overlay positioning and transitions - Component state toggling styles (`.loading` class behavior)
- Responsive skeleton layouts - Responsive skeleton layouts
- Accessibility overrides for `prefers-reduced-motion` - Accessibility overrides for `prefers-reduced-motion`
3. **`./templates/partials/navigation/language-selector.html`** 3. **`./templates/partials/navigation/language-selector.html`**
- Add HTMX swap timing modifiers - Add HTMX swap timing modifiers
- Add skeleton loader indicator reference - Add hyperscript to toggle `.loading` class on component wrappers
- Add hyperscript for skeleton show/hide events - Coordinate skeleton state transitions with HTMX events
4. **`./templates/language-switch.html`** 4. **`./templates/language-switch.html`**
- May need to coordinate OOB swap timing - May need to coordinate OOB swap timing
@@ -259,10 +280,10 @@ Create/modify the following files:
- Import skeleton.css if created separately - Import skeleton.css if created separately
- Add HTMX animation class styles - Add HTMX animation class styles
6. **Include skeleton loader in main template** (wherever CV content lives) 6. **Ensure component wrappers are properly structured**
- Add `<div id="skeleton-loader" class="skeleton-overlay">...</div>` - Each component should have `.component-wrapper` class
- Position over content area - Components contain both `.actual-content` and `.skeleton-content`
- Initially hidden, shown during language switch - Skeleton content is hidden by default, shown when `.loading` class is added
</output> </output>
@@ -270,10 +291,11 @@ Create/modify the following files:
Before declaring complete, perform these tests: Before declaring complete, perform these tests:
**1. Visual Verification:** **1. Visual Verification:**
- [ ] Click EN button: Content fades out → Skeleton appears → New content fades in - [ ] Click EN button: Content fades out → Skeleton appears inside components → New content fades in
- [ ] Click ES button: Same smooth transition with skeleton - [ ] Click ES button: Same smooth transition with skeleton
- [ ] Skeleton boxes pulse/shimmer smoothly - [ ] Skeleton boxes pulse/shimmer smoothly inside each component
- [ ] Skeleton roughly matches CV layout (recognizable structure) - [ ] Skeleton roughly matches CV layout (recognizable structure)
- [ ] Each component toggles between actual content and skeleton state
- [ ] No flashing or jarring jumps during transition - [ ] No flashing or jarring jumps during transition
**2. Timing Verification:** **2. Timing Verification:**
@@ -314,7 +336,8 @@ Before declaring complete, perform these tests:
- [ ] Page refresh during skeleton display - recovers gracefully - [ ] Page refresh during skeleton display - recovers gracefully
**Success Indicators:** **Success Indicators:**
Skeleton loaders appear during language transitions Components toggle between actual content and skeleton states during language transitions
✅ Skeleton states appear INSIDE each component (not as separate overlay)
✅ Pulsing animation is smooth and subtle ✅ Pulsing animation is smooth and subtle
✅ Total transition time feels professional (500-700ms) ✅ Total transition time feels professional (500-700ms)
✅ No jarring content jumps or flashes ✅ No jarring content jumps or flashes
@@ -324,16 +347,17 @@ Before declaring complete, perform these tests:
</verification> </verification>
<success_criteria> <success_criteria>
1. Skeleton loaders with pulsing animation appear during language switching 1. Each CV component toggles between actual content and skeleton state during language switching
2. Three-phase transition: fade-out → skeleton → fade-in 2. Skeleton loaders with pulsing animation appear INSIDE components (not as separate overlay)
3. Total transition time: 500-700ms (feels premium, not sluggish) 3. Three-phase transition: fade-out → skeleton → fade-in
4. Skeleton structure roughly matches CV layout (recognizable) 4. Total transition time: 500-700ms (feels premium, not sluggish)
5. Animations are GPU-accelerated and buttery smooth (60fps) 5. Skeleton structure roughly matches CV layout (recognizable)
6. Respects `prefers-reduced-motion` accessibility setting 6. Animations are GPU-accelerated and buttery smooth (60fps)
7. Handles rapid clicking without breaking or visual glitches 7. Respects `prefers-reduced-motion` accessibility setting
8. Works consistently across modern browsers and device sizes 8. Handles rapid clicking without breaking or visual glitches
9. Code follows existing project patterns and conventions 9. Works consistently across modern browsers and device sizes
10. Implementation is maintainable and doesn't overcomplicate the codebase 10. Code follows existing project patterns and conventions
11. Implementation is maintainable and doesn't overcomplicate the codebase
</success_criteria> </success_criteria>
<references> <references>