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:
juanatsap
2025-11-10 14:00:32 +00:00
parent eda746407e
commit 18db4011f8
3 changed files with 852 additions and 190 deletions
+183 -85
View File
@@ -224,7 +224,8 @@ iconify-icon {
justify-content: space-between;
width: 75px;
height: 30px;
background: rgba(255,255,255,0.1);
background: #e0e0e0;
border: 2px solid #d0d0d0;
border-radius: 15px;
padding: 0 6px;
transition: all 0.3s ease;
@@ -250,6 +251,7 @@ iconify-icon {
.icon-toggle input:checked + .icon-toggle-slider {
background: #27ae60;
border-color: #229954;
}
.icon-toggle-slider .icon-left,
@@ -271,18 +273,22 @@ iconify-icon {
.icon-toggle input:not(:checked) + .icon-toggle-slider .icon-left {
color: #333;
font-weight: bold;
}
.icon-toggle input:not(:checked) + .icon-toggle-slider .icon-right {
color: rgba(255,255,255,0.3);
color: #999;
opacity: 0.5;
}
.icon-toggle input:checked + .icon-toggle-slider .icon-left {
color: rgba(255,255,255,0.3);
color: rgba(255,255,255,0.4);
opacity: 0.5;
}
.icon-toggle input:checked + .icon-toggle-slider .icon-right {
color: #333;
color: #fff;
font-weight: bold;
}
.icon-toggle input:focus + .icon-toggle-slider {
@@ -1424,7 +1430,7 @@ footer {
.action-bar-content {
grid-template-columns: 1fr;
gap: 1rem;
padding: 1rem;
padding: 0rem;
}
.language-toggle,
@@ -1445,6 +1451,22 @@ footer {
font-size: 0.9em;
margin-top: 15px;
}
/* ========== Hide header controls, show in menu ========== */
/* Keep language selector visible in header */
.view-controls-center {
display: none !important;
}
.action-buttons-right {
display: none !important;
}
/* Show controls and actions in hamburger menu */
.menu-controls-section,
.menu-actions-section {
display: block !important;
}
}
.no-print {}
@@ -1666,67 +1688,9 @@ a:focus {
}
/* ===============================================
PRINT STYLES - TWO-PAGE LAYOUT
PRINT STYLES - Handled by print.css
=============================================== */
@media print {
body {
background: white;
margin: 0;
padding: 0;
}
.action-bar {
display: none !important;
}
.cv-page {
box-shadow: none;
border: none;
margin: 0 auto;
transform: scale(1);
max-width: 100%;
page-break-after: always;
page-break-inside: avoid;
}
.cv-page.page-2 {
page-break-after: auto;
}
.page-content {
page-break-inside: avoid;
}
.cv-section {
page-break-inside: avoid;
}
/* Ensure footer only on page 2 */
.page-1 .cv-footer {
display: none !important;
}
.cv-footer {
page-break-inside: avoid;
background: #ddd !important;
color: #333 !important;
-webkit-print-color-adjust: exact;
print-color-adjust: exact;
}
a {
text-decoration: none;
font-weight: 800;
color: inherit;
}
/* Set up proper A4 page dimensions */
@page {
size: A4 portrait;
margin: 0.5in;
}
}
/* All print styles consolidated in /static/css/print.css */
/* ===============================================
SECTION STYLES FOR PAGE 2
@@ -2043,14 +2007,14 @@ a:focus {
/* Show menu when hovering menu itself OR when it has the hover class */
.navigation-menu:hover,
.navigation-menu.menu-hover {
max-height: 800px; /* Fixed height for consistent animation timing */
max-height: calc(100vh - 60px); /* Viewport height minus header + some spacing */
pointer-events: auto; /* Enable pointer events when visible */
opacity: 1;
}
/* Legacy class for JS compatibility - keep for backward compatibility */
.navigation-menu.menu-open {
max-height: 800px;
max-height: calc(100vh - 60px);
pointer-events: auto;
opacity: 1;
}
@@ -2090,16 +2054,15 @@ a:focus {
}
/* Menu item action controls (Expand All, Collapse All) */
.menu-item-action span {
width: calc(100% - 65px);
text-align: center;
}
/* Removed centered text styling - action items now behave like regular menu items */
/* Remove extra padding - all menu items should align consistently */
/* Submenu styles - hover triggered */
/* Submenu styles - hover triggered, opens to the right */
.menu-item-submenu {
position: relative;
border-bottom: 1px solid rgba(0, 0, 0, 0.1);
padding: 0 0 1rem 0;
}
.menu-item.has-submenu {
@@ -2109,41 +2072,176 @@ a:focus {
.submenu-arrow {
transition: transform 0.2s ease;
margin-left: auto;
}
/* Show submenu on hover */
/* Rotate arrow slightly on hover */
.menu-item-submenu:hover .submenu-arrow {
transform: rotate(180deg);
transform: translateX(3px);
}
.submenu-content {
background-color: rgba(0, 0, 0, 0.02);
max-height: 0;
overflow: hidden;
position: fixed; /* Changed from absolute to fixed to break out of parent overflow */
left: 278px; /* Slight overlap with menu to eliminate any gap */
background: #ffffff;
box-shadow: 2px 2px 10px rgba(0, 0, 0, 0.15);
border-radius: 8px;
min-width: 250px;
max-width: 300px;
opacity: 0;
transition: max-height 0.4s ease-in-out, opacity 0.3s ease;
visibility: hidden;
transform: translateX(-3px);
transition: all 0.3s ease;
z-index: 1000; /* Higher z-index to appear above everything */
padding: 0.5rem 0;
max-height: calc(100vh - 100px); /* Ensure it fits viewport */
overflow-y: auto; /* Scroll if content is too long */
}
/* Show submenu when hovering the submenu container */
.menu-item-submenu:hover .submenu-content {
max-height: 600px;
/* Show submenu when hovering the submenu container OR the submenu itself */
.menu-item-submenu:hover .submenu-content,
.submenu-content:hover {
opacity: 1;
visibility: visible;
transform: translateX(0);
}
/* Legacy class for JS compatibility */
.menu-item-submenu.submenu-open .submenu-arrow {
transform: rotate(180deg);
transform: translateX(3px);
}
.menu-item-submenu.submenu-open .submenu-content {
max-height: 600px;
opacity: 1;
visibility: visible;
transform: translateX(0);
}
.submenu-content .menu-item {
padding-left: 3rem;
padding: 0.875rem 1.5rem;
font-size: 0.9rem;
border-left-width: 2px;
border-left: 3px solid transparent;
border-radius: 0;
}
.submenu-content .menu-item:first-child {
border-top-left-radius: 8px;
border-top-right-radius: 8px;
}
.submenu-content .menu-item:last-child {
border-bottom-left-radius: 8px;
border-bottom-right-radius: 8px;
}
/* ========== Menu Sections with Separators ========== */
/* Quick Actions section - always visible */
.menu-section-wrapper {
padding: 0.5rem 1.5rem 1rem;
border-bottom: 1px solid rgba(0, 0, 0, 0.1);
}
/* Remove border from last visible section */
.menu-content > *:last-child,
.menu-content > div:last-child {
border-bottom: none !important;
}
/* ========== Menu Controls & Actions (Mobile) ========== */
/* Hidden by default, shown only on mobile (< 900px) */
.menu-controls-section,
.menu-actions-section {
display: none;
padding: 0.5rem 1.5rem 1rem;
border-bottom: 1px solid rgba(0, 0, 0, 0.1);
}
.menu-item-header {
display: flex;
align-items: center;
gap: 1rem;
padding: 0.875rem 0 0.875rem 0;
color: var(--text-dark);
font-size: 0.85rem;
font-weight: 700;
text-transform: uppercase;
letter-spacing: 0.5px;
cursor: default;
}
/* Disable hover effect for headers */
.menu-item-header:hover {
background-color: transparent !important;
color: var(--text-dark) !important;
border-left-color: transparent !important;
}
.menu-item-header iconify-icon {
color: var(--text-gray);
flex-shrink: 0;
}
.menu-item-header:hover iconify-icon {
color: var(--text-gray) !important;
}
.menu-item-header span {
flex: 1;
}
.menu-control-item {
display: flex;
align-items: center;
justify-content: space-between;
padding: 0.75rem 0;
}
.menu-control-label {
display: flex;
align-items: center;
gap: 0.75rem;
color: var(--text-dark);
font-size: 0.9rem;
font-weight: 500;
}
.menu-control-label iconify-icon {
color: var(--text-gray);
}
.menu-action-btn {
display: flex;
align-items: center;
justify-content: center;
gap: 1rem;
padding: 0.875rem 1rem;
margin: 0.25rem 0;
background: rgba(0, 0, 0, 0.03);
border: none;
border-radius: 8px;
color: var(--text-dark);
text-decoration: none;
font-size: 0.9rem;
font-weight: 500;
cursor: pointer;
transition: all 0.2s ease;
width: 100%;
}
.menu-action-btn:hover {
background: rgba(0, 102, 204, 0.08);
color: var(--accent-blue);
text-decoration: none;
}
.menu-action-btn iconify-icon {
color: var(--text-gray);
flex-shrink: 0;
transition: color 0.2s ease;
}
.menu-action-btn:hover iconify-icon {
color: var(--accent-blue);
}
/* Section icons in titles */
+495 -73
View File
@@ -1,31 +1,80 @@
/* Print Styles - A4 Optimized */
/* Print Styles - A4 Optimized - Consolidated & Fixed */
@media print {
/* ===================================
CRITICAL: Print Color Accuracy
=================================== */
* {
-webkit-print-color-adjust: exact !important;
print-color-adjust: exact !important;
color-adjust: exact !important;
}
/* ===================================
PAGE SETUP - A4 with Minimal Margins
=================================== */
@page {
size: A4;
margin: 0;
size: A4 portrait;
margin: 8mm; /* Minimal printer margins */
}
body {
background: white;
margin: 0;
padding: 0;
background: white !important;
margin: 0 !important;
padding: 0 !important;
}
/* Hide non-print elements */
/* ===================================
HIDE NON-PRINT ELEMENTS
=================================== */
.no-print,
.action-bar,
footer {
.navigation-menu,
.hamburger-btn,
footer,
.back-to-top,
.info-button,
.info-modal,
.error-toast,
.cv-sidebar,
.cv-sidebar-left,
.cv-sidebar-right,
.cv-title-badges-header,
.cv-footer {
display: none !important;
}
/* CV Container - full page */
/* Hide ALL icons in print */
iconify-icon,
.section-icon,
.default-company-icon,
.default-project-icon,
.default-course-icon,
.default-award-icon {
display: none !important;
}
/* Hide company/project/course logos */
.company-logo,
.project-icon,
.course-icon,
.award-logo {
display: none !important;
}
/* ===================================
REMOVE ALL SHADOWS & BORDERS (Nuclear Option)
=================================== */
*,
*::before,
*::after {
box-shadow: none !important;
text-shadow: none !important;
}
/* ===================================
CV CONTAINER - Full Page Width
=================================== */
.cv-container {
width: 100%;
max-width: 100%;
@@ -34,53 +83,166 @@
gap: 0;
}
.cv-container.theme-clean {
padding: 0;
}
/* ===================================
CV PAPER - Reduced Padding (20mm → 12mm)
=================================== */
.cv-paper {
width: 210mm;
min-height: 297mm;
background: white;
padding: 20mm;
box-shadow: none;
margin: 0;
width: 100%;
min-height: auto !important;
background: white !important;
padding: 12mm !important; /* Reduced from 20mm */
box-shadow: none !important;
border: none !important;
margin: 0 !important;
page-break-after: auto;
transform: none !important;
}
/* Force page breaks where needed */
/* ===================================
CV PAGE - Remove All Decorations & Page Breaks
=================================== */
.cv-page,
.theme-clean .cv-page {
box-shadow: none !important;
border: none !important;
background: white !important;
margin: 0 !important;
padding: 0 !important;
transform: scale(1) !important;
max-width: 100% !important;
page-break-after: auto !important; /* Let content flow naturally */
page-break-inside: auto !important; /* Allow breaking inside */
}
.cv-page.page-2 {
page-break-after: auto !important;
}
/* ===================================
PAGE CONTENT GRID - Allow Natural Flow
=================================== */
.page-content {
page-break-inside: auto !important; /* Allow content to break naturally */
display: block !important; /* Remove grid for print */
}
/* ===================================
PAGE BREAKS - Optimized for Content Flow
=================================== */
.page-break {
page-break-after: always;
page-break-after: auto !important; /* Remove forced page breaks */
break-after: auto !important;
}
/* Sections CAN break across pages - flow naturally */
.cv-section {
page-break-inside: auto !important;
break-inside: auto !important;
page-break-before: auto !important; /* No forced breaks before sections */
page-break-after: auto !important; /* No forced breaks after sections */
}
/* Keep individual items together */
.avoid-break,
.cv-section,
.experience-item,
.project-item {
page-break-inside: avoid;
.project-item,
.course-item,
.award-item {
page-break-inside: avoid !important;
break-inside: avoid !important;
}
/* Header */
/* Experience section should flow into Awards - no page break */
#experience {
page-break-after: auto !important;
}
/* ===================================
HEADER - Reduced Spacing
=================================== */
.cv-header {
page-break-after: avoid;
margin-bottom: 15mm;
margin-bottom: 8mm !important; /* Reduced from 15mm */
}
.cv-header-content {
gap: 1.5rem !important;
}
.cv-name {
font-size: 20pt;
margin-bottom: 4pt;
}
.cv-title {
font-size: 12pt;
}
.cv-photo {
width: 80px;
height: 80px;
border-width: 2px;
.years-experience,
.cv-experience-years {
font-size: 10pt;
}
/* ===================================
PHOTO - FIXED ASPECT RATIO (3:4 Portrait)
=================================== */
.cv-photo {
width: 60px !important;
height: 80px !important; /* Maintains 3:4 ratio */
object-fit: contain !important; /* Show full photo, no crop */
border: none !important; /* Remove border */
box-shadow: none !important;
margin: 10px 15px 10px 10px !important;
page-break-inside: avoid;
}
.cv-photo img {
width: 100%;
height: 100%;
object-fit: contain !important;
}
/* ===================================
INTRO TEXT
=================================== */
.intro-text {
font-size: 9pt;
line-height: 1.5;
margin-top: 3mm !important;
}
/* ===================================
SECTIONS - REDUCED SPACING (48px → 19px)
=================================== */
.cv-section {
margin-bottom: 5mm !important; /* ~19px, down from 48px */
margin-top: 7mm !important; /* More breathing space between sections */
}
/* Sections */
.section-title {
font-size: 12pt;
margin-top: 10mm;
font-size: 12pt !important; /* Equalized size for all titles */
font-weight: 600 !important;
margin-top: 0 !important;
margin-bottom: 1mm !important; /* Minimal bottom margin - matches Training/Skills */
page-break-after: avoid;
border-bottom: 0.5pt solid #dddddd !important;
padding-bottom: 2mm !important;
padding-top: 2mm !important; /* Breathing space above */
line-height: 1.3 !important; /* Consistent line height */
}
/* Languages and References need more breathing space below title */
#languages .section-title,
#references .section-title {
margin-bottom: 3mm !important; /* More space for lists */
}
.section-icon {
display: none; /* Hide section icons in print */
}
.summary-text {
@@ -88,40 +250,174 @@
line-height: 1.5;
}
/* Experience */
/* ===================================
EXPERIENCE - REDUCED SPACING (60px → 26px)
=================================== */
.experience-item {
margin-bottom: 8mm;
padding-bottom: 8mm;
margin-bottom: 4mm !important; /* ~15px, down from 40px */
padding-bottom: 3mm !important; /* ~11px, down from 32px */
border-bottom: 0.5pt solid #dddddd !important;
}
.experience-item:last-child {
border-bottom: none !important;
margin-bottom: 0 !important; /* Remove bottom margin from last experience */
padding-bottom: 2mm !important; /* Minimal padding */
}
.position {
font-size: 10pt;
margin-bottom: 2pt;
}
.company {
.company,
.company-link {
font-size: 9pt;
}
.experience-period {
.experience-period,
.experience-location,
.experience-duration {
font-size: 8pt;
}
.short-desc,
.responsibilities li {
font-size: 9pt;
line-height: 1.4;
.short-desc {
font-size: 9pt !important;
line-height: 1.4 !important;
margin-top: 1mm !important;
margin-bottom: 1mm !important;
}
.responsibilities {
margin-top: 2mm;
}
.responsibilities li {
font-size: 9pt !important;
line-height: 1.4 !important;
margin-bottom: 1mm;
}
/* Ensure all experience content is properly sized */
.experience-item p,
.experience-item div {
font-size: 9pt !important;
line-height: 1.4 !important;
}
/* All logos already hidden above - no exceptions */
/* ===================================
PROJECTS & COURSES
=================================== */
.project-item,
.course-item,
.award-item {
display: block !important; /* Remove flex/grid layouts for print */
margin-bottom: 4mm !important;
padding-bottom: 3mm !important;
border-bottom: 0.5pt solid #dddddd !important;
}
.project-item:last-child,
.course-item:last-child,
.award-item:last-child {
border-bottom: none !important; /* Remove border from last item */
}
/* Projects footer - "See all projects" link */
.projects-footer {
margin-top: 4mm !important;
padding-top: 3mm !important;
text-align: center !important;
font-size: 9pt !important;
border-top: 0.5pt solid #dddddd !important; /* Single separator */
}
.projects-footer p {
margin: 0 !important;
padding: 2mm 0 !important;
}
.projects-footer a {
color: #0066cc !important;
text-decoration: none !important;
font-weight: 600 !important;
}
/* Icons/logos already hidden - display: none above */
/* Consistent item titles */
.project-title,
.course-title,
.award-item strong,
.course-item strong {
font-size: 10pt !important;
font-weight: 600 !important;
line-height: 1.3 !important;
display: block !important;
margin-bottom: 1mm !important;
}
.project-desc,
.course-desc,
.award-desc {
font-size: 9pt !important;
line-height: 1.4 !important;
margin-top: 1mm !important;
}
/* Course/Project headers - match Experience spacing */
.course-header,
.project-header {
margin-bottom: 0.5mm !important; /* Minimal spacing */
}
.course-item small,
.project-item small {
font-size: 8pt !important;
color: #666 !important;
display: inline !important; /* Inline like experience dates */
margin-top: 0 !important;
margin-left: 0.5mm !important;
}
/* Course title styling to match experience */
.course-title {
font-size: 10pt !important;
font-weight: 600 !important;
line-height: 1.3 !important;
margin-bottom: 0.5mm !important;
}
/* Course metadata (date, location) - match experience style */
.course-period,
.course-location,
.course-separator {
font-size: 8pt !important;
color: #666 !important;
display: inline !important;
}
/* Ensure all text and paragraphs in course items are properly sized */
.course-item p,
.course-item div,
.project-item p,
.project-item div {
font-size: 9pt !important;
line-height: 1.4 !important;
margin-top: 1mm !important;
margin-bottom: 1mm !important;
}
.project-technologies,
.technologies {
font-size: 8pt;
}
.company-logo {
width: 25px;
height: 25px;
}
/* Education, Skills, etc */
/* ===================================
EDUCATION & SKILLS
=================================== */
.degree,
.skill-title,
.project-name {
@@ -134,64 +430,190 @@
font-size: 8.5pt;
}
/* Certifications & Awards */
.cert-item,
.award-item {
font-size: 8.5pt;
margin-bottom: 2mm;
.education-item {
margin-bottom: 3mm;
}
/* Languages */
/* ===================================
CERTIFICATIONS & AWARDS
=================================== */
.cert-item,
.award-item {
margin-bottom: 3mm !important;
padding-bottom: 2mm !important;
}
.award-item strong,
.cert-item strong {
font-size: 10pt !important;
font-weight: 600 !important;
display: block !important;
margin-bottom: 1mm !important;
}
.award-item small,
.cert-item small {
font-size: 8pt !important;
color: #666 !important;
}
/* Ensure all award/cert content is properly sized */
.award-item p,
.award-item div,
.cert-item p,
.cert-item div {
font-size: 9pt !important;
line-height: 1.4 !important;
margin-top: 1mm !important;
margin-bottom: 1mm !important;
}
/* ===================================
LANGUAGES
=================================== */
.languages-list {
grid-template-columns: repeat(3, 1fr);
display: grid;
grid-template-columns: repeat(2, 1fr);
gap: 2mm;
}
.language-item {
font-size: 8.5pt;
font-size: 9pt !important;
line-height: 1.3 !important;
margin-bottom: 1mm !important;
}
/* Contact info */
.language-item small {
font-size: 8pt;
}
/* ===================================
REFERENCES & OTHER
=================================== */
.reference-item,
.other-content {
font-size: 9pt !important;
line-height: 1.3 !important;
margin-bottom: 1mm !important;
}
/* ===================================
CONTACT INFO
=================================== */
.cv-contact {
font-size: 8.5pt;
grid-template-columns: repeat(2, 1fr);
gap: 2mm;
}
/* Links - print URLs */
/* ===================================
CLEAN THEME - Full Width Main Content
=================================== */
.theme-clean .page-content,
.page-content {
display: block !important;
grid-template-columns: 1fr !important;
}
.theme-clean .cv-main,
.cv-main {
grid-column: 1 !important;
padding: 0 !important;
max-width: 100% !important;
}
/* ===================================
LINKS - Print Styling
=================================== */
a {
color: #0066cc;
text-decoration: none;
font-weight: 600;
}
/* Ensure borders print */
.cv-header {
border-bottom: 1.5pt solid #2d2d2d !important;
/* ===================================
BADGES - Hidden for minimal print
=================================== */
.current-badge,
.expired-badge,
.maintained-badge {
display: none !important;
}
.section-title {
border-bottom: 0.5pt solid #dddddd !important;
/* ===================================
CV LENGTH TOGGLE - Force Short Version for Print Friendly
=================================== */
/* Show short descriptions */
.cv-short .short-desc {
display: block !important;
font-size: 9pt;
line-height: 1.5;
margin-top: 2mm;
}
.experience-item {
border-bottom: 0.5pt solid #dddddd !important;
}
.experience-item:last-child {
border-bottom: none !important;
}
/* Print both short and long version content */
/* Hide long-only content (detailed descriptions) */
.cv-short .long-only,
.long-only {
display: none !important;
}
/* Hide responsibilities (detailed bullet points) */
.cv-short .responsibilities,
.responsibilities {
display: none !important;
}
/* Long version rules (should not apply, but just in case) */
.cv-long .short-desc {
display: none !important;
}
.cv-long .long-only {
display: block !important;
}
.cv-short .short-desc {
display: none !important;
.cv-long .responsibilities {
display: block !important;
}
/* Always print full CV in long mode */
.cv-long .short-desc {
display: none !important;
/* ===================================
THEME CLEAN - Minimal Print Mode
=================================== */
/* All sidebars, headers, footers already hidden above */
/* Main content takes full width */
/* ===================================
COLLAPSIBLE SECTIONS - Force Open
=================================== */
details {
display: block !important;
}
summary {
display: block !important;
list-style: none !important;
}
summary::after,
summary .section-title::after,
.sidebar-section summary::after {
display: none !important; /* Hide collapse indicators */
}
details > *:not(summary) {
display: block !important;
opacity: 1 !important;
max-height: none !important;
transform: none !important;
}
/* ===================================
ENSURE PROPER TEXT RENDERING
=================================== */
body,
.cv-paper {
font-smoothing: antialiased;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
}
}
+174 -32
View File
@@ -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();
}
});