// Linear Portfolio Template JavaScript document.addEventListener('DOMContentLoaded', function() { // Mobile menu toggle const mobileMenuButton = document.querySelector('.mobile-menu-button'); const mobileMenu = document.querySelector('.mobile-menu'); if (mobileMenuButton && mobileMenu) { mobileMenuButton.addEventListener('click', function() { mobileMenu.classList.toggle('hidden'); }); } // Smooth scrolling for navigation links document.querySelectorAll('a[href^="#"]').forEach(anchor => { anchor.addEventListener('click', function (e) { e.preventDefault(); const target = document.querySelector(this.getAttribute('href')); if (target) { target.scrollIntoView({ behavior: 'smooth', block: 'start' }); // Close mobile menu if open if (mobileMenu && !mobileMenu.classList.contains('hidden')) { mobileMenu.classList.add('hidden'); } } }); }); // Form submission handling const contactForm = document.querySelector('#contact form'); if (contactForm) { contactForm.addEventListener('submit', function(e) { e.preventDefault(); // Get form data const formData = new FormData(this); const name = formData.get('name'); const email = formData.get('email'); const company = formData.get('company'); const project = formData.get('project'); // Simple validation if (!name || !email || !project) { showNotification('Please fill in all required fields.', 'error'); return; } // Email validation const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/; if (!emailRegex.test(email)) { showNotification('Please enter a valid email address.', 'error'); return; } // Simulate form submission showNotification('Sending message...', 'info'); setTimeout(() => { showNotification(`Thanks ${name}! Your message has been sent.`, 'success'); this.reset(); }, 2000); }); } // Notification system function showNotification(message, type = 'info') { // Remove existing notification const existingNotification = document.querySelector('.notification'); if (existingNotification) { existingNotification.remove(); } // Create notification element const notification = document.createElement('div'); notification.className = 'notification'; // Set colors based on type let bgColor, borderColor; switch (type) { case 'success': bgColor = '#f0fdf4'; borderColor = '#bbf7d0'; break; case 'error': bgColor = '#fef2f2'; borderColor = '#fecaca'; break; default: bgColor = '#f8fafc'; borderColor = '#e2e8f0'; } notification.style.background = bgColor; notification.style.border = `1px solid ${borderColor}`; notification.innerHTML = `
${message}
`; // Add to page document.body.appendChild(notification); // Show notification setTimeout(() => notification.classList.add('show'), 100); // Auto hide after 5 seconds setTimeout(() => { if (notification && notification.parentElement) { notification.classList.remove('show'); setTimeout(() => notification.remove(), 300); } }, 5000); // Initialize feather icons in notification if (typeof feather !== 'undefined') { feather.replace(); } } // Intersection Observer for animations const observerOptions = { threshold: 0.1, rootMargin: '0px 0px -50px 0px' }; const observer = new IntersectionObserver((entries) => { entries.forEach(entry => { if (entry.isIntersecting) { entry.target.style.opacity = '1'; entry.target.style.transform = 'translateY(0)'; } }); }, observerOptions); // Observe elements for animation document.querySelectorAll('section').forEach(section => { section.style.opacity = '0'; section.style.transform = 'translateY(30px)'; section.style.transition = 'opacity 0.8s ease-out, transform 0.8s ease-out'; observer.observe(section); }); // Header scroll effect const header = document.querySelector('header'); let lastScrollTop = 0; window.addEventListener('scroll', function() { const scrollTop = window.pageYOffset || document.documentElement.scrollTop; if (scrollTop > 100) { header.classList.add('bg-white/95', 'backdrop-blur-sm', 'shadow-sm'); } else { header.classList.remove('shadow-sm'); } lastScrollTop = scrollTop; }); // Typing animation for hero text function typeWriter(element, text, speed = 50) { let i = 0; element.innerHTML = ''; function type() { if (i < text.length) { element.innerHTML += text.charAt(i); i++; setTimeout(type, speed); } } type(); } // Initialize typing animation on hero section const heroTitle = document.querySelector('#home h1'); if (heroTitle) { const originalText = heroTitle.innerHTML; const spans = heroTitle.querySelectorAll('span'); if (spans.length > 0) { typeWriter(spans[0], spans[0].textContent, 100); } } // Lazy loading for images const images = document.querySelectorAll('img'); const imageObserver = new IntersectionObserver((entries, observer) => { entries.forEach(entry => { if (entry.isIntersecting) { const img = entry.target; img.setAttribute('loaded', ''); observer.unobserve(img); } }); }); images.forEach(img => { img.setAttribute('loading', ''); imageObserver.observe(img); }); // Keyboard navigation support document.addEventListener('keydown', function(e) { // ESC key closes mobile menu if (e.key === 'Escape' && mobileMenu && !mobileMenu.classList.contains('hidden')) { mobileMenu.classList.add('hidden'); } }); // Performance optimization: Debounce scroll events function debounce(func, wait) { let timeout; return function executedFunction(...args) { const later = () => { clearTimeout(timeout); func(...args); }; clearTimeout(timeout); timeout = setTimeout(later, wait); }; } // Apply debounce to scroll handler const debouncedScrollHandler = debounce(function() { const scrollTop = window.pageYOffset || document.documentElement.scrollTop; if (scrollTop > 100) { header.classList.add('shadow-sm'); } else { header.classList.remove('shadow-sm'); } }, 10); window.addEventListener('scroll', debouncedScrollHandler); // Initialize feather icons if (typeof feather !== 'undefined') { feather.replace(); } console.log('Linear Portfolio Template initialized successfully!'); });