Toggle sol/luna en el navbar que permite cambiar entre tema oscuro (por defecto) y tema claro. Preferencia guardada en localStorage. Incluye overrides de CSS para todos los componentes con colores hardcodeados, inversión de logo, y transición suave entre temas. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
216 lines
7.6 KiB
JavaScript
216 lines
7.6 KiB
JavaScript
/**
|
|
* Gestoría LP — Main JavaScript
|
|
* Handles: mobile menu, smooth scroll, navbar scroll effect, scroll animations
|
|
*/
|
|
|
|
document.addEventListener('DOMContentLoaded', function () {
|
|
|
|
// ---- DOM References ----
|
|
const navbar = document.getElementById('navbar');
|
|
const navToggle = document.getElementById('navToggle');
|
|
const navMenu = document.getElementById('navMenu');
|
|
const body = document.body;
|
|
|
|
// Create overlay element for mobile menu backdrop
|
|
const overlay = document.createElement('div');
|
|
overlay.classList.add('navbar__overlay');
|
|
document.body.appendChild(overlay);
|
|
|
|
// ================================================================
|
|
// 1. Mobile Hamburger Menu Toggle
|
|
// ================================================================
|
|
function openMenu() {
|
|
navToggle.classList.add('active');
|
|
navMenu.classList.add('active');
|
|
overlay.classList.add('active');
|
|
navToggle.setAttribute('aria-expanded', 'true');
|
|
body.style.overflow = 'hidden';
|
|
}
|
|
|
|
function closeMenu() {
|
|
navToggle.classList.remove('active');
|
|
navMenu.classList.remove('active');
|
|
overlay.classList.remove('active');
|
|
navToggle.setAttribute('aria-expanded', 'false');
|
|
body.style.overflow = '';
|
|
}
|
|
|
|
if (navToggle) {
|
|
navToggle.addEventListener('click', function () {
|
|
if (navMenu.classList.contains('active')) {
|
|
closeMenu();
|
|
} else {
|
|
openMenu();
|
|
}
|
|
});
|
|
}
|
|
|
|
// Close menu when clicking overlay
|
|
overlay.addEventListener('click', closeMenu);
|
|
|
|
// Close menu when pressing Escape
|
|
document.addEventListener('keydown', function (e) {
|
|
if (e.key === 'Escape' && navMenu.classList.contains('active')) {
|
|
closeMenu();
|
|
}
|
|
});
|
|
|
|
// ================================================================
|
|
// 2. Dropdown Toggle (mobile + touch devices)
|
|
// ================================================================
|
|
var dropdownItems = document.querySelectorAll('.navbar__item--dropdown');
|
|
dropdownItems.forEach(function (item) {
|
|
var link = item.querySelector('.navbar__link--dropdown');
|
|
if (link) {
|
|
link.addEventListener('click', function (e) {
|
|
// Always prevent default on the dropdown parent link
|
|
e.preventDefault();
|
|
e.stopPropagation();
|
|
item.classList.toggle('active');
|
|
});
|
|
}
|
|
});
|
|
|
|
// Close dropdown when clicking outside (desktop)
|
|
document.addEventListener('click', function (e) {
|
|
dropdownItems.forEach(function (item) {
|
|
if (!item.contains(e.target)) {
|
|
item.classList.remove('active');
|
|
}
|
|
});
|
|
});
|
|
|
|
// ================================================================
|
|
// 3. Smooth Scroll for Anchor Links
|
|
// ================================================================
|
|
var anchorLinks = document.querySelectorAll('a[href^="#"]');
|
|
anchorLinks.forEach(function (link) {
|
|
// Skip dropdown toggle links — they have their own handler
|
|
if (link.classList.contains('navbar__link--dropdown')) return;
|
|
|
|
link.addEventListener('click', function (e) {
|
|
var targetId = this.getAttribute('href');
|
|
if (targetId === '#') return;
|
|
|
|
var target = document.querySelector(targetId);
|
|
if (target) {
|
|
e.preventDefault();
|
|
closeMenu();
|
|
|
|
var navHeight = navbar ? navbar.offsetHeight : 0;
|
|
var targetPos = target.getBoundingClientRect().top + window.pageYOffset - navHeight;
|
|
|
|
window.scrollTo({
|
|
top: targetPos,
|
|
behavior: 'smooth'
|
|
});
|
|
}
|
|
});
|
|
});
|
|
|
|
// ================================================================
|
|
// 4. Navbar Background/Shadow on Scroll
|
|
// ================================================================
|
|
var lastScrollY = 0;
|
|
|
|
function handleNavbarScroll() {
|
|
var scrollY = window.pageYOffset || document.documentElement.scrollTop;
|
|
|
|
if (scrollY > 20) {
|
|
navbar.classList.add('navbar--scrolled');
|
|
} else {
|
|
navbar.classList.remove('navbar--scrolled');
|
|
}
|
|
|
|
lastScrollY = scrollY;
|
|
}
|
|
|
|
if (navbar) {
|
|
window.addEventListener('scroll', handleNavbarScroll, { passive: true });
|
|
// Run once on load
|
|
handleNavbarScroll();
|
|
}
|
|
|
|
// ================================================================
|
|
// 5. Scroll Animations (Testimonials & fade-up elements)
|
|
// ================================================================
|
|
function animateOnScroll() {
|
|
// Testimonial cards
|
|
var testimonialCards = document.querySelectorAll('.testimonial-card:not(.animate-in)');
|
|
testimonialCards.forEach(function (card, index) {
|
|
var rect = card.getBoundingClientRect();
|
|
var windowHeight = window.innerHeight;
|
|
|
|
if (rect.top < windowHeight * 0.88) {
|
|
// Stagger the animation
|
|
setTimeout(function () {
|
|
card.classList.add('animate-in');
|
|
}, index * 120);
|
|
}
|
|
});
|
|
|
|
// Generic fade-up elements
|
|
var fadeElements = document.querySelectorAll('.fade-up:not(.visible)');
|
|
fadeElements.forEach(function (el) {
|
|
var rect = el.getBoundingClientRect();
|
|
var windowHeight = window.innerHeight;
|
|
|
|
if (rect.top < windowHeight * 0.88) {
|
|
el.classList.add('visible');
|
|
}
|
|
});
|
|
}
|
|
|
|
window.addEventListener('scroll', animateOnScroll, { passive: true });
|
|
// Run once on load to catch elements already in viewport
|
|
animateOnScroll();
|
|
|
|
// ================================================================
|
|
// 6. Close mobile dropdown on window resize to desktop
|
|
// ================================================================
|
|
window.addEventListener('resize', function () {
|
|
if (window.innerWidth > 768) {
|
|
closeMenu();
|
|
dropdownItems.forEach(function (item) {
|
|
item.classList.remove('active');
|
|
});
|
|
}
|
|
});
|
|
|
|
// ================================================================
|
|
// 7. Dark / Light Theme Toggle
|
|
// ================================================================
|
|
var themeToggle = document.getElementById('themeToggle');
|
|
var themeIcon = document.getElementById('themeIcon');
|
|
|
|
function setTheme(theme) {
|
|
document.documentElement.setAttribute('data-theme', theme);
|
|
localStorage.setItem('theme', theme);
|
|
if (themeIcon) {
|
|
themeIcon.className = theme === 'light' ? 'fas fa-sun' : 'fas fa-moon';
|
|
}
|
|
// Update theme-color meta tag
|
|
var metaTheme = document.querySelector('meta[name="theme-color"]');
|
|
if (metaTheme) {
|
|
metaTheme.setAttribute('content', theme === 'light' ? '#f5f5f5' : '#0a0a0a');
|
|
}
|
|
}
|
|
|
|
// Determine initial theme: localStorage > system preference > dark
|
|
var savedTheme = localStorage.getItem('theme');
|
|
if (savedTheme) {
|
|
setTheme(savedTheme);
|
|
} else if (window.matchMedia && window.matchMedia('(prefers-color-scheme: light)').matches) {
|
|
setTheme('light');
|
|
}
|
|
// else: default is dark (no data-theme attribute needed)
|
|
|
|
if (themeToggle) {
|
|
themeToggle.addEventListener('click', function () {
|
|
var current = document.documentElement.getAttribute('data-theme');
|
|
setTheme(current === 'light' ? 'dark' : 'light');
|
|
});
|
|
}
|
|
|
|
});
|