refactor: wrap content in zoom-wrapper to fix footer gap issue

- Created zoom-wrapper div around cv-container
- Zoom now applies to wrapper only, footer adjusts naturally below
- Footer no longer scaled, stays at normal size
- Fixes gap between content and footer at low zoom levels
- Reduced back-to-top button size (35px default, grows to 50px on hover)
- Cleaner separation of concerns for zoom functionality
This commit is contained in:
juanatsap
2025-11-12 15:24:09 +00:00
parent 3ea0d5598e
commit 994716e452
3 changed files with 22 additions and 23 deletions
+10 -9
View File
@@ -491,6 +491,13 @@ iconify-icon {
100% { transform: rotate(360deg); } 100% { transform: rotate(360deg); }
} }
/* Zoom Wrapper - wraps cv-container for zoom functionality */
.zoom-wrapper {
transform-origin: top center; /* Scale from top center */
transition: transform 0.08s linear; /* Smooth zoom response */
will-change: transform; /* Optimize for transforms */
}
/* Main CV Container */ /* Main CV Container */
.cv-container { .cv-container {
width: 100%; width: 100%;
@@ -498,9 +505,6 @@ iconify-icon {
margin: 0 auto; margin: 0 auto;
padding: 20px 0 0 0; /* Top padding to prevent sticky action bar overlap */ padding: 20px 0 0 0; /* Top padding to prevent sticky action bar overlap */
display: block; /* Changed from flex */ display: block; /* Changed from flex */
transform-origin: top center; /* Scale from top center - container stays anchored at top */
transition: transform 0.08s linear; /* Smooth, immediate zoom response */
will-change: transform; /* Hint browser to optimize for transforms */
} }
/* Clean theme - no sidebars, centered content */ /* Clean theme - no sidebars, centered content */
@@ -1700,9 +1704,6 @@ a:focus {
padding: 20px 0; padding: 20px 0;
margin: 0; margin: 0;
grid-column: 1 / -1; /* Span all columns */ grid-column: 1 / -1; /* Span all columns */
transform-origin: top center; /* Scale from top center - footer stays anchored at top */
transition: transform 0.08s linear; /* Smooth, immediate zoom response */
will-change: transform; /* Hint browser to optimize for transforms */
} }
.footer-content { .footer-content {
@@ -2578,8 +2579,8 @@ html {
position: fixed; position: fixed;
bottom: 2rem; bottom: 2rem;
right: 2rem; right: 2rem;
width: 50px; width: 35px; /* Smaller default size */
height: 50px; height: 35px;
background: var(--black-bar); background: var(--black-bar);
color: white; color: white;
border: none; border: none;
@@ -2596,7 +2597,7 @@ html {
.back-to-top:hover { .back-to-top:hover {
opacity: 1; opacity: 1;
transform: translateY(-3px); transform: translateY(-3px) scale(1.43); /* Grow to ~50px on hover */
box-shadow: 0 6px 16px rgba(0, 0, 0, 0.4); box-shadow: 0 6px 16px rgba(0, 0, 0, 0.4);
background: #3a3a3a; background: #3a3a3a;
} }
+6 -11
View File
@@ -273,9 +273,9 @@
function initZoomControl() { function initZoomControl() {
const slider = document.getElementById('zoom-slider'); const slider = document.getElementById('zoom-slider');
const resetBtn = document.getElementById('zoom-reset'); const resetBtn = document.getElementById('zoom-reset');
const cvPaper = document.querySelector('.cv-paper'); const zoomWrapper = document.getElementById('zoom-wrapper');
if (!slider || !cvPaper) return; if (!slider || !zoomWrapper) return;
// On mobile, always use 100% zoom (zoom control is hidden anyway) // On mobile, always use 100% zoom (zoom control is hidden anyway)
if (isMobileView()) { if (isMobileView()) {
@@ -347,20 +347,15 @@
* @param {boolean} saveToStorage - Whether to persist to localStorage * @param {boolean} saveToStorage - Whether to persist to localStorage
*/ */
function applyZoom(zoomValue, saveToStorage = true) { function applyZoom(zoomValue, saveToStorage = true) {
const cvContainer = document.querySelector('.cv-container'); const zoomWrapper = document.getElementById('zoom-wrapper');
const cvFooter = document.querySelector('.cv-footer'); if (!zoomWrapper) return;
if (!cvContainer) return;
// Convert percentage to scale factor (100 = 1.0, 150 = 1.5, etc.) // Convert percentage to scale factor (100 = 1.0, 150 = 1.5, etc.)
const scale = zoomValue / 100; const scale = zoomValue / 100;
requestAnimationFrame(() => { requestAnimationFrame(() => {
// Use transform: scale() for true visual zoom without constraints // Apply zoom to wrapper - footer adjusts naturally below it
// This allows zoom to extend beyond viewport at >100% zoomWrapper.style.transform = `scale(${scale})`;
cvContainer.style.transform = `scale(${scale})`;
if (cvFooter) {
cvFooter.style.transform = `scale(${scale})`;
}
// Update display // Update display
updateZoomDisplay(zoomValue); updateZoomDisplay(zoomValue);
+6 -3
View File
@@ -344,8 +344,10 @@
</div> </div>
</nav> </nav>
<!-- CV Content Container --> <!-- Zoom Wrapper (for zoom functionality) -->
<div class="cv-container"> <div id="zoom-wrapper" class="zoom-wrapper">
<!-- CV Content Container -->
<div class="cv-container">
<main id="cv-content" <main id="cv-content"
class="cv-paper" class="cv-paper"
role="main" role="main"
@@ -432,7 +434,8 @@
</a> </a>
</div> </div>
</div> </div>
</div> </div> <!-- End cv-container -->
</div> <!-- End zoom-wrapper -->
<!-- PDF Export Modal --> <!-- PDF Export Modal -->
<div id="pdf-modal" class="info-modal no-print" onclick="closePdfModalOnBackdrop(event)"> <div id="pdf-modal" class="info-modal no-print" onclick="closePdfModalOnBackdrop(event)">