// Enhanced theme toggle with smooth transitions const toggle = document.getElementById('themeToggle'); const root = document.documentElement; const saved = localStorage.getItem('theme') || 'dark'; // Apply saved theme if (saved === 'light') { document.documentElement.classList.remove('dark'); } else { document.documentElement.classList.add('dark'); } // Update theme toggle icon const updateThemeIcon = () => { const icon = toggle?.querySelector('i'); if (icon) { icon.setAttribute('data-lucide', document.documentElement.classList.contains('dark') ? 'sun-medium' : 'moon'); if (window.lucide) { lucide.createIcons(); } } }; // Initialize theme icon updateThemeIcon(); toggle?.addEventListener('click', () => { const isDark = document.documentElement.classList.toggle('dark'); localStorage.setItem('theme', isDark ? 'dark' : 'light'); updateThemeIcon(); // Add smooth transition effect document.body.style.transition = 'background-color 0.3s ease, color 0.3s ease'; setTimeout(() => { document.body.style.transition = ''; }, 300); }); // Enhanced tabs logic with smooth transitions and ARIA support document.querySelectorAll('.tab').forEach(btn => { btn.addEventListener('click', () => { // Remove active class and ARIA attributes from all tabs document.querySelectorAll('.tab').forEach(b => { b.classList.remove('active'); b.setAttribute('aria-selected', 'false'); }); // Add active class and ARIA attributes to clicked tab btn.classList.add('active'); btn.setAttribute('aria-selected', 'true'); // Get target diagram const target = btn.getAttribute('data-target'); const targetDiagram = document.querySelector(target); if (targetDiagram) { // Hide all diagrams completely and update ARIA document.querySelectorAll('.diagram').forEach(d => { d.style.opacity = '0'; d.style.transform = 'translateY(20px)'; d.style.display = 'none'; d.setAttribute('aria-hidden', 'true'); d.classList.remove('visible'); }); // Show target diagram with fade in and update ARIA setTimeout(() => { targetDiagram.style.display = 'block'; targetDiagram.classList.add('visible'); targetDiagram.style.opacity = '1'; targetDiagram.style.transform = 'translateY(0)'; targetDiagram.setAttribute('aria-hidden', 'false'); }, 150); } }); }); // Initialize first tab as active and show first diagram document.addEventListener('DOMContentLoaded', () => { // Hide all diagrams except the first one document.querySelectorAll('.diagram').forEach((diagram, index) => { if (index === 0) { // First diagram should be visible diagram.style.display = 'block'; diagram.style.opacity = '1'; diagram.style.transform = 'translateY(0)'; diagram.classList.add('visible'); diagram.setAttribute('aria-hidden', 'false'); } else { // Other diagrams should be hidden diagram.style.display = 'none'; diagram.style.opacity = '0'; diagram.style.transform = 'translateY(20px)'; diagram.classList.remove('visible'); diagram.setAttribute('aria-hidden', 'true'); } }); // Ensure Mermaid diagrams are properly initialized if (window.mermaid) { mermaid.init(undefined, document.querySelectorAll('.mermaid')); } }); // Enhanced metric counters with better performance const counters = document.querySelectorAll('.metric-value'); const easeOutCubic = t => 1 - Math.pow(1 - t, 3); const easeOutQuart = t => 1 - Math.pow(1 - t, 4); const animateCount = (el, to, duration = 1200) => { const start = performance.now(); const from = 0; const isLargeNumber = to > 1000; const easing = isLargeNumber ? easeOutQuart : easeOutCubic; // Ensure the element has proper positioning el.style.position = 'relative'; el.style.zIndex = '2'; el.style.width = '100%'; el.style.textAlign = 'center'; el.style.display = 'block'; const step = (now) => { const p = Math.min(1, (now - start) / duration); const v = Math.floor(from + (to - from) * easing(p)); // Format number with appropriate separators if (to >= 1000000) { el.textContent = (v / 1000000).toFixed(1) + 'M'; } else if (to >= 1000) { el.textContent = (v / 1000).toFixed(1) + 'K'; } else { el.textContent = v.toLocaleString(); } if (p < 1) { requestAnimationFrame(step); } else { // Ensure final value is exact if (to >= 1000000) { el.textContent = (to / 1000000).toFixed(1) + 'M'; } else if (to >= 1000) { el.textContent = (to / 1000).toFixed(1) + 'K'; } else { el.textContent = to.toLocaleString(); } } }; requestAnimationFrame(step); }; // Enhanced intersection observer with better performance const observer = new IntersectionObserver(entries => { entries.forEach(e => { if (e.isIntersecting) { const el = e.target; const count = parseInt(el.dataset.count, 10) || 0; // Add loading state el.classList.add('loading'); // Animate with slight delay for better visual effect setTimeout(() => { animateCount(el, count); el.classList.remove('loading'); }, 200); observer.unobserve(el); } }); }, { threshold: 0.3, rootMargin: '0px 0px -50px 0px' }); // Observe counters counters.forEach(c => observer.observe(c)); // Enhanced VanillaTilt initialization with error handling const initTilt = () => { if (window.VanillaTilt) { try { document.querySelectorAll('[data-tilt]').forEach(el => { VanillaTilt.init(el, { max: 6, speed: 400, glare: true, 'max-glare': 0.1, scale: 1.02, gyroscope: false }); }); } catch (error) { console.warn('VanillaTilt initialization failed:', error); } } }; // Initialize tilt effects initTilt(); // Smooth scrolling for navigation links document.querySelectorAll('a[href^="#"]').forEach(link => { link.addEventListener('click', (e) => { e.preventDefault(); const targetId = link.getAttribute('href'); const targetElement = document.querySelector(targetId); if (targetElement) { const headerHeight = document.querySelector('header')?.offsetHeight || 0; const targetPosition = targetElement.offsetTop - headerHeight - 20; window.scrollTo({ top: targetPosition, behavior: 'smooth' }); } }); }); // Enhanced error handling for external resources const handleResourceError = (resource, fallback) => { resource.addEventListener('error', () => { console.warn(`Failed to load resource, using fallback`); if (fallback) { resource.src = fallback; } }); }; // Handle external script loading errors window.addEventListener('error', (e) => { if (e.target.tagName === 'SCRIPT' && e.target.src) { console.warn(`Script failed to load: ${e.target.src}`); } }); // Performance monitoring const logPerformance = () => { if ('performance' in window) { window.addEventListener('load', () => { setTimeout(() => { const perfData = performance.getEntriesByType('navigation')[0]; console.log(`Page load time: ${perfData.loadEventEnd - perfData.loadEventStart}ms`); }, 0); }); } }; // Initialize performance monitoring logPerformance(); // Add keyboard navigation support document.addEventListener('keydown', (e) => { // Tab navigation for architecture tabs if (e.key === 'ArrowLeft' || e.key === 'ArrowRight') { const activeTab = document.querySelector('.tab.active'); if (activeTab) { const tabs = Array.from(document.querySelectorAll('.tab')); const currentIndex = tabs.indexOf(activeTab); let nextIndex; if (e.key === 'ArrowLeft') { nextIndex = currentIndex > 0 ? currentIndex - 1 : tabs.length - 1; } else { nextIndex = currentIndex < tabs.length - 1 ? currentIndex + 1 : 0; } tabs[nextIndex].click(); tabs[nextIndex].focus(); } } }); // Add loading states for dynamic content const addLoadingState = (element, duration = 1000) => { element.classList.add('loading'); setTimeout(() => { element.classList.remove('loading'); }, duration); }; // Initialize all interactive elements document.addEventListener('DOMContentLoaded', () => { // Add loading states to cards that might load content dynamically document.querySelectorAll('.card').forEach(card => { card.addEventListener('mouseenter', () => { addLoadingState(card, 500); }); }); // Initialize tooltips for chips document.querySelectorAll('.chip').forEach(chip => { chip.setAttribute('title', chip.textContent); }); });