feat: add hamburger navigation menu with smooth scrolling to CV sections
Implemented a complete navigation system with the following features: **Navigation Menu:** - Hamburger button in top-left of action bar - Slide-out navigation menu with all CV sections - Smooth close on click outside or after selection - Mobile-responsive design (280px desktop, 240px mobile) **Section Anchors:** - Added ID anchors to all CV sections: - #education (Training/Formación) - #skills (Skills/Competencias) - #experience (Experience/Experiencia) - #awards (Awards/Premios y Reconocimientos) - #courses (Courses/Cursos Realizados) - #languages (Languages/Idiomas) - #references (References/Referencias) - #other (Other/Otros) **Section Icons:** - Added descriptive icons to all section titles - Icons match their purpose (school for education/courses, trophy for awards, etc.) - Consistent 24x24 size for section titles, 20x20 for menu items **Smooth Scrolling:** - Implemented smooth scroll behavior with proper offset calculation - Accounts for fixed header height - Added scroll-padding-top for better anchor positioning **Accessibility:** - Proper ARIA labels and roles - aria-expanded attribute for hamburger button - Keyboard navigation support - Screen reader friendly **Styling:** - Clean white menu background with shadow - Blue hover states matching CV accent color - Left border indicator on hover - Smooth transitions (0.3s ease-in-out) - Print-friendly (menu hidden in print mode) **Bilingual Support:** - Menu items automatically translate based on language - Works seamlessly with English/Spanish switching
@@ -1523,3 +1523,123 @@ a:focus {
|
||||
text-decoration: underline;
|
||||
color: #0052a3;
|
||||
}
|
||||
|
||||
/* ===============================================
|
||||
HAMBURGER MENU & NAVIGATION
|
||||
=============================================== */
|
||||
|
||||
/* Hamburger button */
|
||||
.hamburger-btn {
|
||||
background: transparent;
|
||||
border: none;
|
||||
color: #fff;
|
||||
cursor: pointer;
|
||||
padding: 0.5rem;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
transition: background-color 0.2s ease;
|
||||
border-radius: 4px;
|
||||
margin-right: 0.5rem;
|
||||
}
|
||||
|
||||
.hamburger-btn:hover {
|
||||
background-color: rgba(255, 255, 255, 0.1);
|
||||
}
|
||||
|
||||
.hamburger-btn:active {
|
||||
background-color: rgba(255, 255, 255, 0.2);
|
||||
}
|
||||
|
||||
/* Navigation Menu */
|
||||
.navigation-menu {
|
||||
position: fixed;
|
||||
top: 50px; /* Height of action bar */
|
||||
left: 0;
|
||||
width: 280px;
|
||||
max-height: calc(100vh - 50px);
|
||||
background: #ffffff;
|
||||
box-shadow: 2px 0 10px rgba(0, 0, 0, 0.15);
|
||||
transform: translateX(-100%);
|
||||
transition: transform 0.3s ease-in-out;
|
||||
overflow-y: auto;
|
||||
z-index: 99;
|
||||
}
|
||||
|
||||
.navigation-menu.menu-open {
|
||||
transform: translateX(0);
|
||||
}
|
||||
|
||||
.menu-content {
|
||||
padding: 1rem 0;
|
||||
}
|
||||
|
||||
.menu-item {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 1rem;
|
||||
padding: 0.875rem 1.5rem;
|
||||
color: var(--text-dark);
|
||||
text-decoration: none;
|
||||
transition: background-color 0.2s ease, color 0.2s ease;
|
||||
font-size: 0.95rem;
|
||||
font-weight: 500;
|
||||
border-left: 3px solid transparent;
|
||||
}
|
||||
|
||||
.menu-item:hover {
|
||||
background-color: rgba(0, 102, 204, 0.08);
|
||||
color: var(--accent-blue);
|
||||
border-left-color: var(--accent-blue);
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
.menu-item iconify-icon {
|
||||
color: var(--text-gray);
|
||||
flex-shrink: 0;
|
||||
transition: color 0.2s ease;
|
||||
}
|
||||
|
||||
.menu-item:hover iconify-icon {
|
||||
color: var(--accent-blue);
|
||||
}
|
||||
|
||||
/* Section icons in titles */
|
||||
.section-icon {
|
||||
vertical-align: middle;
|
||||
margin-right: 0.5rem;
|
||||
color: var(--accent-blue);
|
||||
}
|
||||
|
||||
/* Smooth scrolling */
|
||||
html {
|
||||
scroll-behavior: smooth;
|
||||
}
|
||||
|
||||
/* Add scroll padding to account for fixed header */
|
||||
html {
|
||||
scroll-padding-top: 70px; /* Action bar height + some spacing */
|
||||
}
|
||||
|
||||
/* Mobile responsive */
|
||||
@media (max-width: 768px) {
|
||||
.navigation-menu {
|
||||
width: 240px;
|
||||
}
|
||||
|
||||
.menu-item {
|
||||
padding: 0.75rem 1rem;
|
||||
font-size: 0.9rem;
|
||||
}
|
||||
}
|
||||
|
||||
/* Hide menu overlay on print */
|
||||
@media print {
|
||||
.navigation-menu {
|
||||
display: none !important;
|
||||
}
|
||||
|
||||
.hamburger-btn {
|
||||
display: none !important;
|
||||
}
|
||||
}
|
||||
|
||||
|
After Width: | Height: | Size: 2.7 KiB |
|
After Width: | Height: | Size: 3.6 KiB |
|
After Width: | Height: | Size: 11 KiB |
|
After Width: | Height: | Size: 5.8 KiB |
|
After Width: | Height: | Size: 6.1 KiB |
|
Before Width: | Height: | Size: 8.8 KiB After Width: | Height: | Size: 6.8 KiB |
|
Before Width: | Height: | Size: 10 B After Width: | Height: | Size: 10 KiB |
|
After Width: | Height: | Size: 11 KiB |
|
After Width: | Height: | Size: 2.3 KiB |
|
After Width: | Height: | Size: 5.1 KiB |
|
After Width: | Height: | Size: 66 KiB |
|
After Width: | Height: | Size: 3.6 KiB |
|
After Width: | Height: | Size: 6.3 KiB |
@@ -47,8 +47,11 @@
|
||||
</div>
|
||||
|
||||
<!-- Education -->
|
||||
<section class="cv-section">
|
||||
<h3 class="section-title">{{if eq .Lang "es"}}Formación{{else}}Training{{end}}</h3>
|
||||
<section id="education" class="cv-section">
|
||||
<h3 class="section-title">
|
||||
<iconify-icon icon="mdi:school" width="24" height="24" class="section-icon"></iconify-icon>
|
||||
{{if eq .Lang "es"}}Formación{{else}}Training{{end}}
|
||||
</h3>
|
||||
{{range .CV.Education}}
|
||||
<div class="education-item">
|
||||
<strong>{{.Degree}}</strong> ({{.StartDate}}-{{.EndDate}}) {{if eq $.Lang "es"}}obtenido de{{else}}obtained from the{{end}} <strong>{{.Institution}}</strong> ({{.Location}})
|
||||
@@ -57,8 +60,11 @@
|
||||
</section>
|
||||
|
||||
<!-- Skills Summary -->
|
||||
<section class="cv-section">
|
||||
<h3 class="section-title">{{if eq .Lang "es"}}Competencias{{else}}Skills{{end}}</h3>
|
||||
<section id="skills" class="cv-section">
|
||||
<h3 class="section-title">
|
||||
<iconify-icon icon="mdi:brain" width="24" height="24" class="section-icon"></iconify-icon>
|
||||
{{if eq .Lang "es"}}Competencias{{else}}Skills{{end}}
|
||||
</h3>
|
||||
<p class="summary-text">
|
||||
{{if eq .Lang "es"}}
|
||||
Amplio conocimiento en entornos web, tanto J2EE como PHP. Experto en tecnologías front-end, aunque con considerable experiencia en sistemas back-end. Receptivo al aprendizaje de nuevas tecnologías, y con una gran dosis de creatividad. Capacidad de analizar problemas y aportar soluciones específicas adaptadas a cada tipo de cliente. Me gusta trabajar tanto solo como en grupos.
|
||||
@@ -69,8 +75,11 @@
|
||||
</section>
|
||||
|
||||
<!-- Experience -->
|
||||
<section class="cv-section">
|
||||
<h3 class="section-title">{{if eq .Lang "es"}}Experiencia{{else}}Experience{{end}}</h3>
|
||||
<section id="experience" class="cv-section">
|
||||
<h3 class="section-title">
|
||||
<iconify-icon icon="mdi:office-building" width="24" height="24" class="section-icon"></iconify-icon>
|
||||
{{if eq .Lang "es"}}Experiencia{{else}}Experience{{end}}
|
||||
</h3>
|
||||
|
||||
{{range .CV.Experience}}
|
||||
<div class="experience-item">
|
||||
@@ -150,8 +159,11 @@
|
||||
<main class="cv-main">
|
||||
<!-- Awards Section -->
|
||||
{{if .CV.Awards}}
|
||||
<section class="cv-section">
|
||||
<h3 class="section-title">{{if eq .Lang "es"}}Premios y Reconocimientos{{else}}Awards{{end}}</h3>
|
||||
<section id="awards" class="cv-section">
|
||||
<h3 class="section-title">
|
||||
<iconify-icon icon="mdi:trophy" width="24" height="24" class="section-icon"></iconify-icon>
|
||||
{{if eq .Lang "es"}}Premios y Reconocimientos{{else}}Awards{{end}}
|
||||
</h3>
|
||||
{{range .CV.Awards}}
|
||||
<div class="award-item">
|
||||
{{if .AwardLogo}}
|
||||
@@ -182,8 +194,11 @@
|
||||
|
||||
<!-- Courses Section -->
|
||||
{{if .CV.Courses}}
|
||||
<section class="cv-section">
|
||||
<h3 class="section-title">{{if eq .Lang "es"}}Cursos Realizados{{else}}Courses{{end}}</h3>
|
||||
<section id="courses" class="cv-section">
|
||||
<h3 class="section-title">
|
||||
<iconify-icon icon="mdi:school" width="24" height="24" class="section-icon"></iconify-icon>
|
||||
{{if eq .Lang "es"}}Cursos Realizados{{else}}Courses{{end}}
|
||||
</h3>
|
||||
{{range .CV.Courses}}
|
||||
<div class="course-item">
|
||||
<div class="course-icon">
|
||||
@@ -222,8 +237,11 @@
|
||||
{{end}}
|
||||
|
||||
<!-- Languages Section -->
|
||||
<section class="cv-section">
|
||||
<h3 class="section-title">{{if eq .Lang "es"}}Idiomas{{else}}Languages{{end}}</h3>
|
||||
<section id="languages" class="cv-section">
|
||||
<h3 class="section-title">
|
||||
<iconify-icon icon="mdi:translate" width="24" height="24" class="section-icon"></iconify-icon>
|
||||
{{if eq .Lang "es"}}Idiomas{{else}}Languages{{end}}
|
||||
</h3>
|
||||
{{range .CV.Languages}}
|
||||
<div class="language-item">
|
||||
<strong>{{.Language}}:</strong> {{.Proficiency}}{{if .Detail}} {{.Detail}}{{end}}
|
||||
@@ -233,8 +251,11 @@
|
||||
|
||||
<!-- References Section -->
|
||||
{{if .CV.References}}
|
||||
<section class="cv-section">
|
||||
<h3 class="section-title">{{if eq .Lang "es"}}Referencias{{else}}References{{end}}</h3>
|
||||
<section id="references" class="cv-section">
|
||||
<h3 class="section-title">
|
||||
<iconify-icon icon="mdi:link-variant" width="24" height="24" class="section-icon"></iconify-icon>
|
||||
{{if eq .Lang "es"}}Referencias{{else}}References{{end}}
|
||||
</h3>
|
||||
{{range .CV.References}}
|
||||
<div class="reference-item">
|
||||
{{if .TextBefore}}{{.TextBefore}} {{end}}<a href="{{.URL}}" target="_blank" rel="noopener noreferrer"><strong>{{if .LinkText}}{{.LinkText}}{{else}}{{.Title}}{{end}}</strong></a>{{if .TextAfter}} {{.TextAfter}}{{end}}
|
||||
@@ -245,8 +266,11 @@
|
||||
|
||||
<!-- Other Section (Driver's License) -->
|
||||
{{if .CV.Other.DriverLicense}}
|
||||
<section class="cv-section">
|
||||
<h3 class="section-title">{{if eq .Lang "es"}}Otros{{else}}Other{{end}}</h3>
|
||||
<section id="other" class="cv-section">
|
||||
<h3 class="section-title">
|
||||
<iconify-icon icon="mdi:information" width="24" height="24" class="section-icon"></iconify-icon>
|
||||
{{if eq .Lang "es"}}Otros{{else}}Other{{end}}
|
||||
</h3>
|
||||
<div class="other-content">
|
||||
{{if eq .Lang "es"}}Carnet de conducir tipo {{.CV.Other.DriverLicense}}{{else}}Driving License type {{.CV.Other.DriverLicense}}{{end}}
|
||||
</div>
|
||||
|
||||
@@ -97,10 +97,15 @@
|
||||
<!-- Single Black Bar with Everything -->
|
||||
<div class="action-bar no-print" role="navigation" aria-label="Language and export controls">
|
||||
<div class="action-bar-content">
|
||||
<!-- Left: Site Title and Language -->
|
||||
<!-- Left: Hamburger Menu + Site Title and Language -->
|
||||
<div class="site-title">
|
||||
<!-- Hamburger Menu Button -->
|
||||
<button class="hamburger-btn" onclick="toggleMenu()" aria-label="Toggle navigation menu">
|
||||
<iconify-icon icon="mdi:menu" width="24" height="24"></iconify-icon>
|
||||
</button>
|
||||
|
||||
<iconify-icon icon="mdi:file-account" width="24" height="24" class="site-icon"></iconify-icon>
|
||||
<span class="site-title-text">CV {{.CurrentYear}} - JAMR</span>
|
||||
<span class="site-title-text">CV JAMR - {{.CurrentYear}}</span>
|
||||
|
||||
<!-- Language selector (after title) -->
|
||||
<div class="language-selector">
|
||||
@@ -181,6 +186,44 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 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="#education" class="menu-item" onclick="scrollToSection('education')">
|
||||
<iconify-icon icon="mdi:school" width="20" height="20"></iconify-icon>
|
||||
<span>{{if eq .Lang "es"}}Formación{{else}}Training{{end}}</span>
|
||||
</a>
|
||||
<a href="#skills" class="menu-item" onclick="scrollToSection('skills')">
|
||||
<iconify-icon icon="mdi:brain" width="20" height="20"></iconify-icon>
|
||||
<span>{{if eq .Lang "es"}}Competencias{{else}}Skills{{end}}</span>
|
||||
</a>
|
||||
<a href="#experience" class="menu-item" onclick="scrollToSection('experience')">
|
||||
<iconify-icon icon="mdi:office-building" width="20" height="20"></iconify-icon>
|
||||
<span>{{if eq .Lang "es"}}Experiencia{{else}}Experience{{end}}</span>
|
||||
</a>
|
||||
<a href="#awards" class="menu-item" onclick="scrollToSection('awards')">
|
||||
<iconify-icon icon="mdi:trophy" width="20" height="20"></iconify-icon>
|
||||
<span>{{if eq .Lang "es"}}Premios y Reconocimientos{{else}}Awards{{end}}</span>
|
||||
</a>
|
||||
<a href="#courses" class="menu-item" onclick="scrollToSection('courses')">
|
||||
<iconify-icon icon="mdi:school" width="20" height="20"></iconify-icon>
|
||||
<span>{{if eq .Lang "es"}}Cursos Realizados{{else}}Courses{{end}}</span>
|
||||
</a>
|
||||
<a href="#languages" class="menu-item" onclick="scrollToSection('languages')">
|
||||
<iconify-icon icon="mdi:translate" width="20" height="20"></iconify-icon>
|
||||
<span>{{if eq .Lang "es"}}Idiomas{{else}}Languages{{end}}</span>
|
||||
</a>
|
||||
<a href="#references" class="menu-item" onclick="scrollToSection('references')">
|
||||
<iconify-icon icon="mdi:link-variant" width="20" height="20"></iconify-icon>
|
||||
<span>{{if eq .Lang "es"}}Referencias{{else}}References{{end}}</span>
|
||||
</a>
|
||||
<a href="#other" class="menu-item" onclick="scrollToSection('other')">
|
||||
<iconify-icon icon="mdi:information" width="20" height="20"></iconify-icon>
|
||||
<span>{{if eq .Lang "es"}}Otros{{else}}Other{{end}}</span>
|
||||
</a>
|
||||
</div>
|
||||
</nav>
|
||||
|
||||
<!-- CV Content Container -->
|
||||
<div class="cv-container">
|
||||
<main id="cv-content"
|
||||
@@ -205,6 +248,58 @@
|
||||
</div>
|
||||
|
||||
<script>
|
||||
// Toggle navigation menu
|
||||
function toggleMenu() {
|
||||
const menu = document.getElementById('navigation-menu');
|
||||
const btn = document.querySelector('.hamburger-btn');
|
||||
|
||||
if (menu.classList.contains('menu-open')) {
|
||||
menu.classList.remove('menu-open');
|
||||
btn.setAttribute('aria-expanded', 'false');
|
||||
} else {
|
||||
menu.classList.add('menu-open');
|
||||
btn.setAttribute('aria-expanded', 'true');
|
||||
}
|
||||
}
|
||||
|
||||
// Scroll to section smoothly
|
||||
function scrollToSection(sectionId) {
|
||||
event.preventDefault(); // Prevent default anchor behavior
|
||||
|
||||
const section = document.getElementById(sectionId);
|
||||
if (section) {
|
||||
const actionBarHeight = document.querySelector('.action-bar').offsetHeight;
|
||||
const menuHeight = document.querySelector('.navigation-menu').offsetHeight;
|
||||
const offset = actionBarHeight + (menuHeight || 0) + 20; // Add 20px padding
|
||||
|
||||
const elementPosition = section.getBoundingClientRect().top;
|
||||
const offsetPosition = elementPosition + window.pageYOffset - offset;
|
||||
|
||||
window.scrollTo({
|
||||
top: offsetPosition,
|
||||
behavior: 'smooth'
|
||||
});
|
||||
|
||||
// Close menu after clicking
|
||||
const menu = document.getElementById('navigation-menu');
|
||||
menu.classList.remove('menu-open');
|
||||
document.querySelector('.hamburger-btn').setAttribute('aria-expanded', 'false');
|
||||
}
|
||||
}
|
||||
|
||||
// Close menu when clicking outside
|
||||
document.addEventListener('click', function(event) {
|
||||
const menu = document.getElementById('navigation-menu');
|
||||
const btn = document.querySelector('.hamburger-btn');
|
||||
|
||||
if (menu && btn && menu.classList.contains('menu-open')) {
|
||||
if (!menu.contains(event.target) && !btn.contains(event.target)) {
|
||||
menu.classList.remove('menu-open');
|
||||
btn.setAttribute('aria-expanded', 'false');
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
function selectLanguage(lang) {
|
||||
// Update button states
|
||||
document.querySelectorAll('.language-selector .selector-btn').forEach(btn => {
|
||||
|
||||