feat: comprehensive print optimization for professional PDF output
Print CSS overhaul: - Fixed photo aspect ratio (60x80, 3:4 portrait) with contain fit - Unified font sizes across all sections (10pt titles, 9pt content, 8pt metadata) - Removed excessive spacing (reduced by 50-70%) - Applied clean theme automatically (no sidebars, icons, logos, badges) - Force short version for concise 5-page output - Natural page breaks (removed forced breaks causing blank spaces) - Consistent section title spacing with proper breathing room - Match Training/Skills spacing pattern across all sections - Fixed Languages and References spacing - Equalized Experience, Courses, Projects, and Awards formatting - Single separator for "See all projects" link UI improvements: - Enhanced icon toggle visibility with better contrast - Reorganized navigation menu structure
This commit is contained in:
+174
-32
@@ -194,19 +194,12 @@
|
||||
<!-- Navigation Menu (Hidden by default) -->
|
||||
<nav id="navigation-menu" class="navigation-menu no-print" role="navigation" aria-label="CV sections">
|
||||
<div class="menu-content">
|
||||
<a href="#" class="menu-item menu-item-action" onclick="expandAllSections(event)">
|
||||
<iconify-icon icon="mdi:arrow-expand-all" width="20" height="20"></iconify-icon>
|
||||
<span>{{if eq .Lang "es"}}Expandir Todo{{else}}Expand All{{end}}</span>
|
||||
</a>
|
||||
<a href="#" class="menu-item menu-item-action" onclick="collapseAllSections(event)">
|
||||
<iconify-icon icon="mdi:arrow-collapse-all" width="20" height="20"></iconify-icon>
|
||||
<span>{{if eq .Lang "es"}}Colapsar Todo{{else}}Collapse All{{end}}</span>
|
||||
</a>
|
||||
<!-- CV Sections - Quick Navigation -->
|
||||
<div class="menu-item-submenu">
|
||||
<a href="#" class="menu-item has-submenu">
|
||||
<iconify-icon icon="mdi:menu" width="20" height="20"></iconify-icon>
|
||||
<span>{{if eq .Lang "es"}}Secciones CV{{else}}CV Sections{{end}}</span>
|
||||
<iconify-icon icon="mdi:chevron-down" width="16" height="16" class="submenu-arrow"></iconify-icon>
|
||||
<iconify-icon icon="mdi:chevron-right" width="16" height="16" class="submenu-arrow"></iconify-icon>
|
||||
</a>
|
||||
<div class="submenu-content">
|
||||
<a href="#education" class="menu-item" onclick="scrollToSection('education')">
|
||||
@@ -247,6 +240,94 @@
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Quick Actions Section -->
|
||||
<div class="menu-section-wrapper">
|
||||
<div class="menu-item menu-item-header">
|
||||
<iconify-icon icon="mdi:cog-outline" width="20" height="20"></iconify-icon>
|
||||
<span>{{if eq .Lang "es"}}Acciones Rápidas{{else}}Quick Actions{{end}}</span>
|
||||
</div>
|
||||
|
||||
<a href="#" class="menu-item menu-item-action" onclick="expandAllSections(event)">
|
||||
<iconify-icon icon="mdi:arrow-expand-all" width="20" height="20"></iconify-icon>
|
||||
<span>{{if eq .Lang "es"}}Expandir Todo{{else}}Expand All{{end}}</span>
|
||||
</a>
|
||||
<a href="#" class="menu-item menu-item-action" onclick="collapseAllSections(event)">
|
||||
<iconify-icon icon="mdi:arrow-collapse-all" width="20" height="20"></iconify-icon>
|
||||
<span>{{if eq .Lang "es"}}Colapsar Todo{{else}}Collapse All{{end}}</span>
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<!-- View Controls in menu (visible only on mobile < 900px) -->
|
||||
<div class="menu-controls-section">
|
||||
<div class="menu-item menu-item-header">
|
||||
<iconify-icon icon="mdi:tune-variant" width="20" height="20"></iconify-icon>
|
||||
<span>{{if eq .Lang "es"}}Controles de Vista{{else}}View Controls{{end}}</span>
|
||||
</div>
|
||||
|
||||
<!-- CV Length toggle -->
|
||||
<div class="menu-control-item">
|
||||
<label class="menu-control-label">
|
||||
<iconify-icon icon="mdi:file-document-outline" width="20" height="20"></iconify-icon>
|
||||
<span>{{if eq .Lang "es"}}Longitud{{else}}Length{{end}}</span>
|
||||
</label>
|
||||
<label class="icon-toggle">
|
||||
<input type="checkbox" id="lengthToggleMenu" onchange="toggleCVLength()">
|
||||
<span class="icon-toggle-slider">
|
||||
<iconify-icon icon="mdi:file-document-outline" width="16" height="16" class="icon-left"></iconify-icon>
|
||||
<iconify-icon icon="mdi:file-document-multiple-outline" width="16" height="16" class="icon-right"></iconify-icon>
|
||||
</span>
|
||||
</label>
|
||||
</div>
|
||||
|
||||
<!-- Logo toggle -->
|
||||
<div class="menu-control-item">
|
||||
<label class="menu-control-label">
|
||||
<iconify-icon icon="mdi:image-multiple-outline" width="20" height="20"></iconify-icon>
|
||||
<span>{{if eq .Lang "es"}}Logos{{else}}Logos{{end}}</span>
|
||||
</label>
|
||||
<label class="icon-toggle">
|
||||
<input type="checkbox" id="logoToggleMenu" checked onchange="toggleLogos()">
|
||||
<span class="icon-toggle-slider">
|
||||
<iconify-icon icon="mdi:image-off-outline" width="16" height="16" class="icon-left"></iconify-icon>
|
||||
<iconify-icon icon="mdi:image-multiple-outline" width="16" height="16" class="icon-right"></iconify-icon>
|
||||
</span>
|
||||
</label>
|
||||
</div>
|
||||
|
||||
<!-- Theme toggle -->
|
||||
<div class="menu-control-item">
|
||||
<label class="menu-control-label">
|
||||
<iconify-icon icon="mdi:page-layout-sidebar-left" width="20" height="20"></iconify-icon>
|
||||
<span>{{if eq .Lang "es"}}Vista{{else}}View{{end}}</span>
|
||||
</label>
|
||||
<label class="icon-toggle">
|
||||
<input type="checkbox" id="themeToggleMenu" onchange="toggleTheme()">
|
||||
<span class="icon-toggle-slider">
|
||||
<iconify-icon icon="mdi:page-layout-sidebar-left" width="16" height="16" class="icon-left"></iconify-icon>
|
||||
<iconify-icon icon="mdi:page-layout-body" width="16" height="16" class="icon-right"></iconify-icon>
|
||||
</span>
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Action Buttons in menu (visible only on mobile < 900px) -->
|
||||
<div class="menu-actions-section">
|
||||
<div class="menu-item menu-item-header">
|
||||
<iconify-icon icon="mdi:lightning-bolt" width="20" height="20"></iconify-icon>
|
||||
<span>{{if eq .Lang "es"}}Acciones{{else}}Actions{{end}}</span>
|
||||
</div>
|
||||
|
||||
<a class="menu-action-btn" href="/export/pdf?lang={{.Lang}}" download>
|
||||
<iconify-icon icon="mdi:download" width="20" height="20"></iconify-icon>
|
||||
<span>{{if eq .Lang "es"}}Descargar como PDF{{else}}Download as PDF{{end}}</span>
|
||||
</a>
|
||||
|
||||
<button class="menu-action-btn" onclick="printFriendly()">
|
||||
<iconify-icon icon="mdi:leaf" width="20" height="20"></iconify-icon>
|
||||
<span>{{if eq .Lang "es"}}Imprimir amigable{{else}}Print Friendly{{end}}</span>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</nav>
|
||||
|
||||
@@ -361,6 +442,17 @@
|
||||
menu.classList.remove('menu-hover');
|
||||
hamburgerBtn.setAttribute('aria-expanded', 'false');
|
||||
});
|
||||
|
||||
// Position submenu dynamically
|
||||
const submenuTrigger = document.querySelector('.menu-item-submenu');
|
||||
const submenuContent = document.querySelector('.submenu-content');
|
||||
|
||||
if (submenuTrigger && submenuContent) {
|
||||
submenuTrigger.addEventListener('mouseenter', function() {
|
||||
const triggerRect = submenuTrigger.getBoundingClientRect();
|
||||
submenuContent.style.top = `${triggerRect.top}px`;
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
// Legacy toggle function - kept for compatibility
|
||||
@@ -486,13 +578,21 @@
|
||||
}
|
||||
|
||||
function toggleCVLength() {
|
||||
const toggle = document.getElementById('lengthToggle');
|
||||
const headerToggle = document.getElementById('lengthToggle');
|
||||
const menuToggle = document.getElementById('lengthToggleMenu');
|
||||
const paper = document.querySelector('.cv-paper');
|
||||
|
||||
// Get the state from whichever toggle was clicked
|
||||
const isChecked = event?.target?.id === 'lengthToggleMenu' ? menuToggle?.checked : headerToggle?.checked;
|
||||
|
||||
// Sync both toggles
|
||||
if (headerToggle) headerToggle.checked = isChecked;
|
||||
if (menuToggle) menuToggle.checked = isChecked;
|
||||
|
||||
// Save current scroll position
|
||||
const currentScrollY = window.scrollY || window.pageYOffset;
|
||||
|
||||
if (toggle.checked) {
|
||||
if (isChecked) {
|
||||
paper.classList.add('cv-long');
|
||||
paper.classList.remove('cv-short');
|
||||
localStorage.setItem('cv-length', 'long');
|
||||
@@ -509,13 +609,21 @@
|
||||
}
|
||||
|
||||
function toggleLogos() {
|
||||
const toggle = document.getElementById('logoToggle');
|
||||
const headerToggle = document.getElementById('logoToggle');
|
||||
const menuToggle = document.getElementById('logoToggleMenu');
|
||||
const paper = document.querySelector('.cv-paper');
|
||||
|
||||
// Get the state from whichever toggle was clicked
|
||||
const isChecked = event?.target?.id === 'logoToggleMenu' ? menuToggle?.checked : headerToggle?.checked;
|
||||
|
||||
// Sync both toggles
|
||||
if (headerToggle) headerToggle.checked = isChecked;
|
||||
if (menuToggle) menuToggle.checked = isChecked;
|
||||
|
||||
// Save current scroll position
|
||||
const currentScrollY = window.scrollY || window.pageYOffset;
|
||||
|
||||
if (toggle.checked) {
|
||||
if (isChecked) {
|
||||
paper.classList.add('show-logos');
|
||||
localStorage.setItem('cv-logos', 'show');
|
||||
} else {
|
||||
@@ -530,10 +638,18 @@
|
||||
}
|
||||
|
||||
function toggleTheme() {
|
||||
const toggle = document.getElementById('themeToggle');
|
||||
const headerToggle = document.getElementById('themeToggle');
|
||||
const menuToggle = document.getElementById('themeToggleMenu');
|
||||
const container = document.querySelector('.cv-container');
|
||||
|
||||
if (toggle.checked) {
|
||||
// Get the state from whichever toggle was clicked
|
||||
const isChecked = event?.target?.id === 'themeToggleMenu' ? menuToggle?.checked : headerToggle?.checked;
|
||||
|
||||
// Sync both toggles
|
||||
if (headerToggle) headerToggle.checked = isChecked;
|
||||
if (menuToggle) menuToggle.checked = isChecked;
|
||||
|
||||
if (isChecked) {
|
||||
container.classList.add('theme-clean');
|
||||
localStorage.setItem('cv-theme', 'clean');
|
||||
} else {
|
||||
@@ -542,25 +658,38 @@
|
||||
}
|
||||
}
|
||||
|
||||
// Print Friendly - applies Clean theme before printing
|
||||
// Print Friendly - Apply Clean Theme + Short Version for minimal printing
|
||||
function printFriendly() {
|
||||
const container = document.querySelector('.cv-container');
|
||||
const paper = document.querySelector('.cv-paper');
|
||||
const wasClean = container.classList.contains('theme-clean');
|
||||
const wasLong = paper.classList.contains('cv-long');
|
||||
|
||||
// Apply clean theme for printing
|
||||
// Apply clean theme for minimal print (no sidebars, no header, no icons)
|
||||
if (!wasClean) {
|
||||
container.classList.add('theme-clean');
|
||||
}
|
||||
|
||||
// Print
|
||||
window.print();
|
||||
// Force SHORT version for print (hide detailed content)
|
||||
paper.classList.remove('cv-long');
|
||||
paper.classList.add('cv-short');
|
||||
|
||||
// Restore original theme after print dialog
|
||||
// Small delay to let CSS apply
|
||||
setTimeout(() => {
|
||||
if (!wasClean) {
|
||||
container.classList.remove('theme-clean');
|
||||
}
|
||||
}, 100);
|
||||
window.print();
|
||||
|
||||
// Restore original theme and length after print dialog closes
|
||||
setTimeout(() => {
|
||||
if (!wasClean) {
|
||||
container.classList.remove('theme-clean');
|
||||
}
|
||||
// Restore original length
|
||||
if (wasLong) {
|
||||
paper.classList.remove('cv-short');
|
||||
paper.classList.add('cv-long');
|
||||
}
|
||||
}, 100);
|
||||
}, 50);
|
||||
}
|
||||
|
||||
// Initialize with saved preferences or defaults
|
||||
@@ -595,30 +724,43 @@
|
||||
|
||||
// Restore CV length preference
|
||||
const savedLength = localStorage.getItem('cv-length') || 'short';
|
||||
if (savedLength === 'long') {
|
||||
const lengthChecked = savedLength === 'long';
|
||||
if (lengthChecked) {
|
||||
paper.classList.add('cv-long');
|
||||
paper.classList.remove('cv-short');
|
||||
document.getElementById('lengthToggle').checked = true;
|
||||
} else {
|
||||
paper.classList.add('cv-short');
|
||||
paper.classList.remove('cv-long');
|
||||
document.getElementById('lengthToggle').checked = false;
|
||||
}
|
||||
// Sync both header and menu toggles
|
||||
const headerLengthToggle = document.getElementById('lengthToggle');
|
||||
const menuLengthToggle = document.getElementById('lengthToggleMenu');
|
||||
if (headerLengthToggle) headerLengthToggle.checked = lengthChecked;
|
||||
if (menuLengthToggle) menuLengthToggle.checked = lengthChecked;
|
||||
|
||||
// Restore logos preference
|
||||
const savedLogos = localStorage.getItem('cv-logos') || 'show';
|
||||
if (savedLogos === 'show') {
|
||||
const logosChecked = savedLogos === 'show';
|
||||
if (logosChecked) {
|
||||
paper.classList.add('show-logos');
|
||||
document.getElementById('logoToggle').checked = true;
|
||||
} else {
|
||||
paper.classList.remove('show-logos');
|
||||
document.getElementById('logoToggle').checked = false;
|
||||
}
|
||||
// Sync both header and menu toggles
|
||||
const headerLogoToggle = document.getElementById('logoToggle');
|
||||
const menuLogoToggle = document.getElementById('logoToggleMenu');
|
||||
if (headerLogoToggle) headerLogoToggle.checked = logosChecked;
|
||||
if (menuLogoToggle) menuLogoToggle.checked = logosChecked;
|
||||
|
||||
// Restore theme preference
|
||||
const savedTheme = localStorage.getItem('cv-theme') || 'default';
|
||||
if (savedTheme === 'clean') {
|
||||
document.getElementById('themeToggle').checked = true;
|
||||
const themeChecked = savedTheme === 'clean';
|
||||
// Sync both header and menu toggles
|
||||
const headerThemeToggle = document.getElementById('themeToggle');
|
||||
const menuThemeToggle = document.getElementById('themeToggleMenu');
|
||||
if (headerThemeToggle) headerThemeToggle.checked = themeChecked;
|
||||
if (menuThemeToggle) menuThemeToggle.checked = themeChecked;
|
||||
if (themeChecked) {
|
||||
toggleTheme();
|
||||
}
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user