# Zoom Implementation - Technical Documentation

## Overview

This document describes the technical implementation of the custom zoom feature in the CV application, including architectural decisions, constraints, and solutions to specific challenges.

## Implementation Summary

- **Range**: 25% - 175% (centered at 100%)
- **Technology**: CSS `zoom` property on wrapper element
- **Fixed Elements**: Action bar, zoom control, info button, back-to-top button
- **Viewport Behavior**: Horizontal scroll enabled when zoomed > 100%

## Technical Architecture

### 1. Core Zoom Application

**Location**: `static/js/main.js` - `applyZoom()` function

```javascript
function applyZoom(zoomValue, saveToStorage = true) {
    const zoomWrapper = document.getElementById('zoom-wrapper');
    const zoomLevel = zoomValue / 100;

    // Apply CSS zoom to wrapper
    zoomWrapper.style.zoom = zoomLevel;

    // Allow horizontal expansion when zoomed > 100%
    if (zoomLevel > 1) {
        zoomWrapper.style.minWidth = `${window.innerWidth}px`;
    } else {
        zoomWrapper.style.minWidth = '';
    }

    // Apply inverse zoom to fixed buttons
    const inverseZoom = 1 / zoomLevel;
    document.getElementById('back-to-top').style.zoom = inverseZoom;
    document.getElementById('info-button').style.zoom = inverseZoom;
}
```

### 2. HTML Structure

**Location**: `templates/index.html`

```html
<!-- Fixed elements (NOT affected by zoom) -->
<div class="action-bar">...</div>
<nav class="navigation-menu">...</nav>

<!-- Zoomable content wrapper -->
<div id="zoom-wrapper" class="zoom-wrapper">
    <div class="cv-container">
        <main class="cv-paper">
            <!-- CV content here -->
        </main>
    </div>
</div>

<!-- Fixed elements (NOT affected by zoom) -->
<footer>...</footer>
<button id="back-to-top">...</button>
<button id="info-button">...</button>
<div id="zoom-control">...</div>
```

## Critical Design Decisions

### Decision 1: CSS `zoom` vs `transform: scale()`

**Choice**: CSS `zoom` property

**Why**:
- **Layout impact**: `zoom` affects actual layout dimensions, making scrollbars appear naturally
- **Footer positioning**: With `zoom`, footer automatically follows zoomed content
- **Browser behavior**: Mimics native browser zoom behavior more closely

**Why NOT `transform: scale()`**:
- Only visual transformation, doesn't affect layout
- Creates infinite gray space below content
- Requires complex margin calculations to position footer
- Doesn't trigger natural scrollbar behavior

### Decision 2: Inverse Zoom for Fixed Elements

**Problem**: CSS `zoom` on parent can affect fixed-position children in some rendering contexts

**Solution**: Apply inverse zoom to elements that must stay constant size

```javascript
const inverseZoom = 1 / zoomLevel;
button.style.zoom = inverseZoom;
```

**Example**:
- Content zoom: 150% (1.5)
- Button inverse zoom: 66.67% (1/1.5)
- Result: Button appears at 100% (1.5 × 0.667 = 1.0)

**Affected Elements**:
- `#back-to-top` button
- `#info-button` button
- Note: `#zoom-control` and `.action-bar` are naturally outside zoom context

### Decision 3: Viewport Expansion Strategy

**Problem**: Elements with `width: 100%` stay constrained to viewport even when zoomed

**Solution**: Set `min-width` on zoom wrapper when zoom > 100%

```javascript
if (zoomLevel > 1) {
    zoomWrapper.style.minWidth = `${window.innerWidth}px`;
}
```

**Effect**:
- Content can expand beyond viewport width
- Horizontal scrollbar appears automatically
- User can scroll to see all zoomed content

**CSS Support**:
```css
body {
    overflow-x: auto; /* Enable horizontal scroll */
}
```

## Constraints and Limitations

### 1. Browser Compatibility

**CSS `zoom` property**:
- ✅ Chrome, Edge, Safari: Full support
- ⚠️ Firefox: Supported since Firefox 126 (May 2024)
- Fallback: Content remains at 100% zoom in unsupported browsers

### 2. Range Limitations

**Current Range**: 25% - 175%

**Why centered at 100%**:
- Slider midpoint = (25 + 175) / 2 = 100
- Users expect "reset" to be at slider center
- Provides balanced zoom-in/zoom-out range

**Why NOT 50% - 200%**:
- 50% minimum would make slider center = 125%, confusing UX
- Current range provides adequate zoom range while maintaining intuitive controls

### 3. Fixed Element Positioning

**Elements that MUST stay outside zoom-wrapper**:
- Action bar (navigation)
- Zoom control
- Fixed buttons (info, back-to-top)
- Footer

**If placed inside zoom-wrapper**:
- Would scale with content
- Requires inverse zoom application (added complexity)
- Footer positioning becomes unreliable

### 4. Performance Considerations

**CSS `zoom` Performance**:
- Generally performant (GPU-accelerated in modern browsers)
- Can cause reflow when changed
- Mitigated by `requestAnimationFrame()` wrapper

**Optimization**:
```javascript
requestAnimationFrame(() => {
    zoomWrapper.style.zoom = zoomLevel;
    // Other DOM modifications
});
```

### 5. Mobile Behavior

**Current Implementation**: Zoom control hidden on mobile (≤768px viewport)

**Rationale**:
- Mobile browsers provide native pinch-to-zoom
- Custom zoom control redundant on touch devices
- Saves screen space on small viewports

**Code**:
```javascript
function isMobileView() {
    return window.innerWidth <= 768;
}
```

## Edge Cases Handled

### 1. Zoom Persistence

**Behavior**: Zoom level saved to `localStorage`

```javascript
localStorage.setItem('cv-zoom', zoomValue.toString());
```

**On Page Load**: Restore saved zoom level
```javascript
const savedZoom = localStorage.getItem('cv-zoom');
if (savedZoom) {
    applyZoom(parseInt(savedZoom, 10), false);
}
```

### 2. Responsive Resize

**Problem**: User resizes window while zoomed

**Solution**: Recalculate min-width on window resize

```javascript
window.addEventListener('resize', function() {
    // Debounced resize handler
    if (isMobileView()) {
        applyZoom(100, false); // Reset on mobile
    }
});
```

### 3. Reset Button Green Indicator

**Feature**: Reset button turns green when zoom ≠ 100%

**Implementation**:
```javascript
if (zoomValue !== 100) {
    resetBtn.classList.add('zoom-not-default');
} else {
    resetBtn.classList.remove('zoom-not-default');
}
```

**CSS**:
```css
.zoom-reset-btn.zoom-not-default:hover {
    background: #74aacd;
}
```

### 4. Keyboard Shortcuts

**Supported Shortcuts**:
- `Ctrl/Cmd + Plus`: Zoom in (+10%)
- `Ctrl/Cmd + Minus`: Zoom out (-10%)
- `Ctrl/Cmd + 0`: Reset to 100%

**Implementation**:
```javascript
document.addEventListener('keydown', function(e) {
    if ((e.ctrlKey || e.metaKey) && !e.shiftKey) {
        if (e.key === '=' || e.key === '+') {
            e.preventDefault();
            incrementZoom(10);
        }
        // ... other shortcuts
    }
});
```

## Bottom Scroll Detection Integration

**Feature**: Info and back-to-top buttons turn green when at page bottom

**Challenge**: Zoom affects scroll calculations

**Solution**: Bottom detection works independently of zoom

```javascript
const scrollHeight = document.documentElement.scrollHeight;
const clientHeight = document.documentElement.clientHeight;
const isAtBottom = (scrollHeight - currentScroll - clientHeight) < 50;
```

**Why it works**: `scrollHeight` and `clientHeight` automatically account for zoomed content dimensions

## Future Considerations

### Potential Enhancements

1. **Smooth Zoom Transitions**
   - Current: Instant zoom change
   - Possible: CSS transition on zoom property
   - Trade-off: May feel laggy for large zoom changes

2. **Zoom Presets**
   - Current: Free-range slider
   - Possible: Preset buttons (50%, 75%, 100%, 125%, 150%)
   - Trade-off: Less flexible, but faster access

3. **Per-Section Zoom**
   - Current: Entire document zooms
   - Possible: Zoom specific sections independently
   - Trade-off: Complex state management

### Known Limitations to Monitor

1. **Firefox < 126**: No CSS zoom support (falls back to 100%)
2. **Print Behavior**: Zoom is reset for print (intentional)
3. **High Zoom Levels**: Text rendering quality may degrade > 175%

## Testing Checklist

- [ ] Zoom in/out with slider
- [ ] Zoom with keyboard shortcuts
- [ ] Reset button functionality
- [ ] Fixed elements stay constant size
- [ ] Horizontal scroll appears when needed
- [ ] Bottom detection works with zoom
- [ ] Green button indicators work correctly
- [ ] localStorage persistence works
- [ ] Mobile: Zoom control hidden
- [ ] Print: Zoom resets properly

## References

- CSS zoom specification: [MDN Web Docs](https://developer.mozilla.org/en-US/docs/Web/CSS/zoom)
- Browser compatibility: [Can I Use - CSS zoom](https://caniuse.com/css-zoom)
- Related: transform scale vs zoom discussion

---

**Last Updated**: 2025-11-12
**Author**: Development Team
**Status**: Implemented and Stable
