Files
cv-site/doc/5-ZOOM-IMPLEMENTATION.md
T
juanatsap 3fa5d1e5ca docs: Rename all documentation with numbered prefixes for clarity
RENAMING:
- README.md → 0-README.md (start here)
- ARCHITECTURE.md → 1-ARCHITECTURE.md (backend overview)
- MODERN-WEB-TECHNIQUES.md → 2-MODERN-WEB-TECHNIQUES.md (frontend architecture)
- API.md → 3-API.md (API reference)
- HYPERSCRIPT-RULES.md → 4-HYPERSCRIPT-RULES.md (conventions)
- ZOOM_IMPLEMENTATION.md → 5-ZOOM-IMPLEMENTATION.md (zoom feature)
- USER_GUIDE.md → 6-USER-GUIDE.md (user docs)
- CUSTOMIZATION.md → 7-CUSTOMIZATION.md (customization guide)
- DEPLOYMENT.md → 8-DEPLOYMENT.md (deployment)
- SECURITY.md → 9-SECURITY.md (security)
- PRIVACY.md → 10-PRIVACY.md (privacy policy)

CROSS-REFERENCE UPDATES:
- Updated all internal links in all documentation files
- Updated README navigation with numbered references
- Added # column to documentation tables
- Made all links use numbered filenames

BENEFITS:
 Clear reading order (0-10)
 Logical progression (overview → technical → user → ops → compliance)
 Easy sorting and navigation
 Professional organization
 Zero broken links
2025-11-18 19:12:50 +00:00

333 lines
8.6 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 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