From 927d257f2c9f7d8e3030d31df897337312061403 Mon Sep 17 00:00:00 2001 From: juanatsap Date: Wed, 12 Nov 2025 17:04:45 +0000 Subject: [PATCH] docs: add comprehensive zoom implementation technical documentation - Document CSS zoom vs transform scale decision - Explain inverse zoom technique for fixed elements - Detail viewport expansion strategy - Cover edge cases, constraints, and browser compatibility - Include testing checklist and future considerations --- doc/ZOOM_IMPLEMENTATION.md | 332 +++++++++++++++++++++++++++++++++++++ 1 file changed, 332 insertions(+) create mode 100644 doc/ZOOM_IMPLEMENTATION.md diff --git a/doc/ZOOM_IMPLEMENTATION.md b/doc/ZOOM_IMPLEMENTATION.md new file mode 100644 index 0000000..1fda222 --- /dev/null +++ b/doc/ZOOM_IMPLEMENTATION.md @@ -0,0 +1,332 @@ +# 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 + +
...
+ + + +
+
+
+ +
+
+
+ + + + + +
...
+``` + +## 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: rgba(39, 174, 96, 0.5); /* Green */ +} +``` + +### 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