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:
@@ -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>
|
||||||
|
|||||||
Reference in New Issue
Block a user