# Implement Skeleton Loader Transitions for Language Switching

<objective>
Implement professional skeleton loader animations when switching between English and Spanish languages in the CV application. The goal is to create a polished, modern user experience similar to FriendKit (reference screenshots provided) where content transitions through skeleton/placeholder states with pulsing animations.

**Visual Reference:** The user provided screenshots showing gray pulsing placeholder boxes that appear during page transitions - these skeleton loaders make the transition feel smooth and intentional while new content loads.

This will transform the current instant language switch into a premium, modern web app experience.
</objective>

<context>
**Current Implementation:**
- Language switching uses HTMX with `/switch-language?lang={en|es}` endpoint
- HTMX performs out-of-band swaps (`hx-swap-oob="innerHTML"`) to update:
  - `#language-selector` (primary target)
  - `#cv-inner-content-page-1`
  - `#cv-inner-content-page-2`
- Currently uses `hx-swap="outerHTML"` which causes instant, jarring content replacement
- No intermediate loading state - content just "pops" from one language to another

**Reference Files:**
@templates/language-switch.html - Server response template with OOB swaps
@templates/partials/navigation/language-selector.html - Language selector buttons
@static/css/main.css - Existing CSS with some transitions already in place

**Tech Stack:**
- HTMX for dynamic content swapping
- Hyperscript for custom behaviors (already in use)
- Vanilla CSS for animations (no external animation libraries)
- Go templates for server-side rendering

**Desired User Experience (from screenshots):**
1. User clicks language button (EN or ES)
2. Current content fades out (200-300ms)
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
5. Skeleton loaders fade out and new content fades in (200-300ms)
6. Total smooth, professional transition feel

**Why Skeleton Loaders Matter:**
- Perceived performance: Users perceive loading as faster when they see progressive feedback
- Modern UX standard: Used by Facebook, LinkedIn, YouTube, and all modern web apps
- Reduces cognitive load: Shows users "something is happening" vs blank screen or instant swap
- Professional polish: Demonstrates attention to detail and quality
</context>

<requirements>
1. **Skeleton Loader Design:**
   - Gray placeholder boxes (`background: #e0e0e0` or similar) matching content structure
   - Pulsing/shimmer animation (subtle opacity or gradient shift)
   - Should roughly match the layout of CV sections (header, skills, experience, etc.)
   - Lightweight and fast to render

2. **Three-Phase Transition:**
   - **Phase 1 (Fade Out):** Current content fades to opacity 0 (250ms)
   - **Phase 2 (Skeleton Display):** Show skeleton loaders with pulse animation
   - **Phase 3 (Fade In):** New content fades from opacity 0 to 1 (250ms)
   - Total transition: ~500-700ms (feels premium, not sluggish)

3. **HTMX Integration:**
   - Use HTMX swap timing modifiers: `swap:250ms settle:250ms`
   - Leverage HTMX request lifecycle events for skeleton state management
   - Use `htmx:beforeRequest` to show skeleton
   - Use `htmx:afterSwap` to hide skeleton and fade in content

4. **CSS Animations:**
   - Create `.skeleton-loader` component classes
   - Pulsing animation using CSS `@keyframes` (opacity or background gradient shift)
   - Animation should be subtle: 1.5s-2s loop, infinite
   - GPU-accelerated properties only (opacity, transform)

5. **Layout Matching:**
   - Skeleton should mirror the actual CV layout structure
   - Consider creating skeleton variants for:
     - Header section (name, title, badges)
     - Skills sidebar sections
     - Experience entries
     - Education entries
   - Doesn't need to be pixel-perfect, just recognizable structure

6. **Performance:**
   - Skeleton rendering should be instant (<16ms)
   - Animations GPU-accelerated for smooth 60fps
   - No layout thrashing during transitions
   - Total transition under 700ms

7. **Accessibility:**
   - Respect `prefers-reduced-motion` - disable pulse animation
   - Add `aria-busy="true"` during loading states
   - Ensure screen readers announce loading state

8. **Consistency:**
   - Use existing project timing patterns (0.2s-0.3s)
   - Match existing design language and color palette
   - Work on mobile and desktop viewports
</requirements>

<implementation>
**Recommended Implementation Strategy:**

**Step 1: Add Skeleton States to Existing Components**

Modify existing CV component templates to include built-in skeleton states:

- `templates/partials/cv-header.html` - Add skeleton state markup inside the component
- `templates/partials/skills-*.html` - Add skeleton placeholders for skill blocks
- `templates/partials/experience-entry.html` - Add skeleton placeholders for experience blocks
- Each component should have both actual content and skeleton markup, toggled via CSS classes

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**

Add to `static/css/main.css` or create `static/css/skeleton.css`:

```css
/* Skeleton loader base styles */
.skeleton {
  background: linear-gradient(90deg, #f0f0f0 25%, #e0e0e0 50%, #f0f0f0 75%);
  background-size: 200% 100%;
  animation: skeleton-pulse 1.5s ease-in-out infinite;
  border-radius: 4px;
}

@keyframes skeleton-pulse {
  0%, 100% { background-position: 200% 0; }
  50% { background-position: 0 0; }
}

/* Component state toggling */
.component-wrapper .actual-content {
  opacity: 1;
  transition: opacity 250ms ease;
}

.component-wrapper .skeleton-content {
  opacity: 0;
  position: absolute;
  top: 0;
  left: 0;
  right: 0;
  pointer-events: none;
  transition: opacity 250ms ease;
}

/* Loading state - toggle content visibility */
.component-wrapper.loading .actual-content {
  opacity: 0;
  pointer-events: none;
}

.component-wrapper.loading .skeleton-content {
  opacity: 1;
  pointer-events: all;
}

/* Skeleton shapes - create boxes matching your layout */
.skeleton-header { height: 120px; margin-bottom: 20px; }
.skeleton-sidebar-item { height: 60px; margin-bottom: 10px; }
.skeleton-content-item { height: 100px; margin-bottom: 15px; }

/* Reduce motion support */
@media (prefers-reduced-motion: reduce) {
  .skeleton { animation: none; background: #e0e0e0; }
}
```

**Step 3: Update Language Selector with HTMX Timing**

Modify `templates/partials/navigation/language-selector.html`:
- Add `hx-swap="outerHTML swap:250ms settle:250ms"`
- Add hyperscript to toggle `.loading` class on component wrappers
- Coordinate timing with component fade-in/out transitions

**Step 4: Ensure Component Wrappers Have Proper Classes**

Verify that CV component wrappers in templates have:
- `.component-wrapper` class for CSS targeting
- Both `.actual-content` and `.skeleton-content` children
- Proper positioning context (relative positioning on wrapper)

**Step 5: HTMX Event Handling with Hyperscript**

Add hyperscript behavior to toggle loading state on components:

```hyperscript
on htmx:beforeRequest from #language-selector
  add .loading to .component-wrapper in #cv-inner-content-page-1
  add .loading to .component-wrapper in #cv-inner-content-page-2
end

on htmx:afterSwap from #language-selector
  wait 100ms  -- Brief delay to ensure content is rendered
  remove .loading from .component-wrapper in #cv-inner-content-page-1
  remove .loading from .component-wrapper in #cv-inner-content-page-2
end
```

**Alternative Approach (CSS-based):**
Use HTMX's automatic class management:
- Add `.component-wrapper` to elements receiving OOB swaps
- Use HTMX classes: `.htmx-swapping` triggers skeleton state
- CSS: `.component-wrapper.htmx-swapping .actual-content { opacity: 0; }`
- CSS: `.component-wrapper.htmx-swapping .skeleton-content { opacity: 1; }`

**Step 6: Content Fade Transitions**

Add fade-in/out to actual content sections:

```css
.cv-page-content-wrapper {
  transition: opacity 250ms ease;
}

.cv-page-content-wrapper.htmx-swapping {
  opacity: 0;
}

.cv-page-content-wrapper.htmx-settling {
  opacity: 1;
}
```

**What to Prioritize:**
1. Add skeleton markup inside each existing component (dual-state structure)
2. Implement CSS toggling between actual content and skeleton content
3. Add pulsing animation to skeleton elements
4. Refine skeleton shapes to better match layout
5. Polish timing and transitions
6. Add accessibility features

**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 make the skeleton identical to real content - approximate is better
- Don't use JavaScript animations - stick to CSS for performance
- Don't make transitions too long - 500-700ms total maximum
- Don't forget to test rapid clicking (skeleton should handle interruptions gracefully)

**Why These Constraints Matter:**
- **Simplicity:** Complex skeletons are harder to maintain when layout changes
- **Performance:** CSS animations are GPU-accelerated; JS animations cause jank
- **UX Research:** 400-700ms is the sweet spot - longer feels broken, shorter feels pointless
- **Resilience:** Users click fast - implementation must handle interruptions without breaking
</implementation>

<output>
Create/modify the following files:

1. **Modify existing component templates** (UPDATE)
   - `./templates/partials/cv-header.html` - Add skeleton state inside component
   - `./templates/partials/skills-*.html` - Add skeleton placeholders
   - `./templates/partials/experience-entry.html` - Add skeleton placeholders
   - Each component gets dual-state structure (actual content + skeleton content)

2. **`./static/css/skeleton.css`** (NEW)
   - Skeleton loader styles
   - Pulsing animation keyframes
   - Component state toggling styles (`.loading` class behavior)
   - Responsive skeleton layouts
   - Accessibility overrides for `prefers-reduced-motion`

3. **`./templates/partials/navigation/language-selector.html`**
   - Add HTMX swap timing modifiers
   - Add hyperscript to toggle `.loading` class on component wrappers
   - Coordinate skeleton state transitions with HTMX events

4. **`./templates/language-switch.html`**
   - May need to coordinate OOB swap timing
   - Ensure skeleton works with all content updates

5. **`./static/css/main.css`**
   - Add content fade-in/out transitions
   - Import skeleton.css if created separately
   - Add HTMX animation class styles

6. **Ensure component wrappers are properly structured**
   - Each component should have `.component-wrapper` class
   - Components contain both `.actual-content` and `.skeleton-content`
   - Skeleton content is hidden by default, shown when `.loading` class is added

</output>

<verification>
Before declaring complete, perform these tests:

**1. Visual Verification:**
- [ ] Click EN button: Content fades out → Skeleton appears inside components → New content fades in
- [ ] Click ES button: Same smooth transition with skeleton
- [ ] Skeleton boxes pulse/shimmer smoothly inside each component
- [ ] Skeleton roughly matches CV layout (recognizable structure)
- [ ] Each component toggles between actual content and skeleton state
- [ ] No flashing or jarring jumps during transition

**2. Timing Verification:**
- [ ] Total transition feels responsive (<700ms)
- [ ] Skeleton appears immediately when language button clicked
- [ ] Content fade-out is smooth (not instant)
- [ ] Content fade-in is smooth after skeleton disappears

**3. Interaction Testing:**
- [ ] Rapidly click between EN and ES - no broken states
- [ ] Click language button while skeleton is showing - handles gracefully
- [ ] Mobile viewport - skeleton works correctly
- [ ] Desktop viewport - skeleton works correctly

**4. Performance Testing:**
- [ ] Open DevTools Performance tab
- [ ] Record during language switch
- [ ] Verify 60fps animation (no dropped frames)
- [ ] No layout thrashing or long tasks
- [ ] GPU acceleration active for animations

**5. Accessibility Testing:**
- [ ] Enable "Reduce motion" in OS settings → Skeleton pulse disabled
- [ ] Screen reader announces loading state
- [ ] Keyboard navigation still works during transitions
- [ ] Focus management doesn't break

**6. Browser Compatibility:**
- [ ] Chrome: Smooth skeleton animations
- [ ] Firefox: Smooth skeleton animations
- [ ] Safari: Smooth skeleton animations
- [ ] Mobile browsers: Touch interactions work correctly

**7. Edge Cases:**
- [ ] Slow network simulation (throttle to 3G) - skeleton visible longer
- [ ] Fast network - skeleton doesn't flash too quickly (minimum display time?)
- [ ] First load vs subsequent switches - consistent behavior
- [ ] Page refresh during skeleton display - recovers gracefully

**Success Indicators:**
✅ 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
✅ Total transition time feels professional (500-700ms)
✅ No jarring content jumps or flashes
✅ Works consistently across browsers and devices
✅ Respects accessibility preferences
✅ Handles rapid interactions gracefully
</verification>

<success_criteria>
1. Each CV component toggles between actual content and skeleton state during language switching
2. Skeleton loaders with pulsing animation appear INSIDE components (not as separate overlay)
3. Three-phase transition: fade-out → skeleton → fade-in
4. Total transition time: 500-700ms (feels premium, not sluggish)
5. Skeleton structure roughly matches CV layout (recognizable)
6. Animations are GPU-accelerated and buttery smooth (60fps)
7. Respects `prefers-reduced-motion` accessibility setting
8. Handles rapid clicking without breaking or visual glitches
9. Works consistently across modern browsers and device sizes
10. Code follows existing project patterns and conventions
11. Implementation is maintainable and doesn't overcomplicate the codebase
</success_criteria>

<references>
**HTMX Documentation:**
- Swap timing: https://htmx.org/attributes/hx-swap/
- Request indicators: https://htmx.org/attributes/hx-indicator/
- CSS transitions: https://htmx.org/examples/animations/

**Skeleton Loader Patterns:**
- Facebook-style skeleton screens
- Modern progressive loading UX patterns
- LinkedIn content placeholders

**Visual Reference (provided by user):**
- FriendKit example pages showing gray pulsing placeholder boxes
- Boxes animate with subtle shimmer/pulse effect
- Content appears progressively after skeleton state
</references>

<additional_context>
**If you can provide the FriendKit code:**
If you have access to the actual CSS/HTML from the FriendKit examples, examining their skeleton loader implementation would be valuable for:
- Exact timing values they use
- Gradient/animation patterns for the pulse effect
- How they structure skeleton markup
- Any clever performance optimizations

However, we can implement an excellent skeleton loader without that code - the visual reference is sufficient to create a premium experience.
</additional_context>
