2025-11-12 17:04:45 +00:00
# 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 {
2025-11-16 12:48:12 +00:00
background : #74aacd ;
2025-11-12 17:04:45 +00:00
}
```
### 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