/** * @license * SPDX-License-Identifier: Apache-2.0 */ document.addEventListener('DOMContentLoaded', () => { const header = document.querySelector('header') as HTMLElement; const nav = document.querySelector('header .main-nav') as HTMLElement; const navLinks = document.querySelectorAll('header .main-nav ul li a'); const hamburgerMenu = document.querySelector('.hamburger-menu') as HTMLButtonElement; const navHeight = header ? header.offsetHeight : 70; // Theme Toggle const themeToggleButton = document.querySelector('.theme-toggle') as HTMLButtonElement; const htmlElement = document.documentElement; function applyTheme(theme: string) { htmlElement.setAttribute('data-theme', theme); if (themeToggleButton) { const icon = themeToggleButton.querySelector('i'); if (icon) { if (theme === 'dark') { icon.classList.remove('fa-moon'); icon.classList.add('fa-sun'); themeToggleButton.setAttribute('aria-label', 'Açık moda geç'); } else { icon.classList.remove('fa-sun'); icon.classList.add('fa-moon'); themeToggleButton.setAttribute('aria-label', 'Karanlık moda geç'); } } } localStorage.setItem('theme', theme); } function toggleTheme() { const currentTheme = htmlElement.getAttribute('data-theme') || 'light'; const newTheme = currentTheme === 'light' ? 'dark' : 'light'; applyTheme(newTheme); } if (themeToggleButton) { themeToggleButton.addEventListener('click', toggleTheme); } // Load saved theme or system preference const savedTheme = localStorage.getItem('theme'); const prefersDark = window.matchMedia && window.matchMedia('(prefers-color-scheme: dark)').matches; if (savedTheme) { applyTheme(savedTheme); } else if (prefersDark) { applyTheme('dark'); } else { applyTheme('light'); // Default to light if no preference } // Header Scroll Effect function handleHeaderScroll() { if (header && window.pageYOffset > 50) { header.classList.add('header-scrolled'); } else if (header) { header.classList.remove('header-scrolled'); } } window.addEventListener('scroll', handleHeaderScroll); handleHeaderScroll(); // Initial check // Hero Headline Type & Delete Effect const headlineTextElement = document.getElementById('headline-text'); const cursorElement = document.querySelector('.hero-content h1 .cursor') as HTMLElement; const sentences = [ "Markanız İçin Benzersiz Dijital Deneyimler Yaratıyorum.", "Kullanıcı Odaklı ve Modern Web Çözümleri.", "Freelance Web Tasarım Uzmanlığı ile Projelerinizi Hayata Geçirin.", "Etkileyici Tasarımlar, Güçlü Teknolojiler.", "Mobil Uyumlu, SEO Dostu Web Siteleri İnşa Ediyorum.", "Web Varlığınızı Sanata Dönüştürelim.", "İşletmenizi Dijitalde Zirveye Taşıyacak Tasarımlar.", "Yaratıcı Fikirler, Somut Sonuçlar.", "Sadece Kod Değil, Deneyim Tasarlıyorum.", "Dijital Kimliğinizi Profesyonel Ellere Emanet Edin.", "Hayallerinizi Koda Dönüştüren Çözümler.", "Teknoloji ve Sanatın Buluştuğu Web Siteleri." ]; let sentenceIndex = 0; let charIndex = 0; let isDeleting = false; const typingSpeed = 100; const deletingSpeed = 50; const delayBetweenSentences = 2000; function animateHeadline() { if (!headlineTextElement || !cursorElement) return; const currentSentence = sentences[sentenceIndex]; cursorElement.style.display = 'inline-block'; if (isDeleting) { headlineTextElement.textContent = currentSentence.substring(0, charIndex - 1); charIndex--; if (charIndex === 0) { isDeleting = false; sentenceIndex = (sentenceIndex + 1) % sentences.length; setTimeout(animateHeadline, 500); } else { setTimeout(animateHeadline, deletingSpeed); } } else { headlineTextElement.textContent = currentSentence.substring(0, charIndex + 1); charIndex++; if (charIndex === currentSentence.length) { isDeleting = true; setTimeout(animateHeadline, delayBetweenSentences); } else { setTimeout(animateHeadline, typingSpeed); } } } if (headlineTextElement && cursorElement) { setTimeout(animateHeadline, 500); } // Hamburger Menu Toggle if (hamburgerMenu && nav) { hamburgerMenu.addEventListener('click', () => { nav.classList.toggle('active'); const isExpanded = nav.classList.contains('active'); hamburgerMenu.setAttribute('aria-expanded', isExpanded.toString()); const icon = hamburgerMenu.querySelector('i'); if (icon) { if (isExpanded) { icon.classList.remove('fa-bars'); icon.classList.add('fa-times'); } else { icon.classList.remove('fa-times'); icon.classList.add('fa-bars'); } } }); } // Smooth scroll for navigation links & close mobile menu on click navLinks.forEach(link => { link.addEventListener('click', function(e) { const href = this.getAttribute('href'); if (href && href.startsWith('#')) { e.preventDefault(); const targetId = href.substring(1); const targetElement = document.getElementById(targetId); if (targetElement) { navLinks.forEach(navLink => navLink.classList.remove('active')); this.classList.add('active'); const elementPosition = targetElement.getBoundingClientRect().top; const currentHeaderHeight = (document.querySelector('header') as HTMLElement)?.offsetHeight || navHeight; const offsetPosition = elementPosition + window.pageYOffset - currentHeaderHeight; window.scrollTo({ top: offsetPosition, behavior: "smooth" }); if (nav && nav.classList.contains('active')) { nav.classList.remove('active'); hamburgerMenu.setAttribute('aria-expanded', 'false'); const icon = hamburgerMenu.querySelector('i'); if (icon) { icon.classList.remove('fa-times'); icon.classList.add('fa-bars'); } } } } }); }); // Sayfa yüklendiğinde ve scroll edildiğinde aktif linki belirle function setActiveLinkOnScroll() { let currentSectionId = ""; const sections = document.querySelectorAll('main section'); const currentHeaderHeight = (document.querySelector('header') as HTMLElement)?.offsetHeight || navHeight; sections.forEach(section => { const sectionHtmlEl = section as HTMLElement; const sectionTop = sectionHtmlEl.offsetTop - currentHeaderHeight - 50; if (window.pageYOffset >= sectionTop) { currentSectionId = sectionHtmlEl.getAttribute('id') || ""; } }); if (sections.length > 0) { const firstSection = sections[0] as HTMLElement; if (window.pageYOffset < firstSection.offsetTop - currentHeaderHeight - 50) { if (firstSection.id === 'hero') currentSectionId = 'hero'; } } navLinks.forEach(link => { link.classList.remove('active'); const linkHref = link.getAttribute('href'); if (linkHref && linkHref.substring(1) === currentSectionId) { link.classList.add('active'); } }); if (!currentSectionId && navLinks.length > 0) { const heroLink = document.querySelector('header .main-nav ul li a[href="#hero"]'); let isAnyLinkActive = false; navLinks.forEach(l => { if(l.classList.contains('active')) isAnyLinkActive = true; }); if(!isAnyLinkActive && heroLink && window.pageYOffset < ((sections[0] as HTMLElement)?.offsetTop - currentHeaderHeight - 50) ) { heroLink.classList.add('active'); } } } const initialActiveLink = document.querySelector('header .main-nav ul li a[href="#hero"]'); if(initialActiveLink && window.pageYOffset < 50) { navLinks.forEach(l => l.classList.remove('active')); initialActiveLink.classList.add('active'); } window.addEventListener('load', setActiveLinkOnScroll); window.addEventListener('scroll', setActiveLinkOnScroll); // Müşteri Yorumları (Testimonials) - Slider const testimonialsData = [ { text: "Serdal Bey ile çalışmak harikaydı! Web sitemiz tam istediğimiz gibi oldu, modern ve kullanıcı dostu. Kesinlikle tavsiye ederim.", author: "Ayşe K.", avatarLetter: "A" }, { text: "Freelancer olarak başladığım bu yolda, profesyonel bir web sitesine ihtiyacım vardı. SerdalWeb, beklentilerimin ötesine geçti. Hızlı ve etkili çözümler için teşekkürler!", author: "Mehmet Y.", avatarLetter: "M" }, { text: "E-ticaret sitemizin yenilenmesi sürecinde Serdal Bey'in uzmanlığı sayesinde dönüşüm oranlarımızda gözle görülür bir artış oldu. Modern tasarımı ve teknik bilgisi takdire şayan.", author: "Elif S.", avatarLetter: "E" }, { text: "Proje boyunca iletişimi çok kuvvetliydi ve her talebimize anında yanıt verdi. Sonuçtan çok memnunuz. 15 yıllık tecrübesi gerçekten fark yaratıyor.", author: "Can B.", avatarLetter: "C" }, { text: "Responsive tasarım konusundaki hassasiyeti ve yaratıcı fikirleri sayesinde mobil kullanıcılarımızdan çok olumlu geri dönüşler alıyoruz.", author: "Zeynep T.", avatarLetter: "Z" }, { text: "WordPress tabanlı sitemizin yönetimini çok kolaylaştırdı. Hem estetik hem de fonksiyonel bir iş çıkardı. Emeğine sağlık.", author: "Ahmet D.", avatarLetter: "A" }, { text: "Modern ve yenilikçi bir bakış açısıyla projemize yaklaştı. SerdalWeb ile çalışmak, dijital varlığımızı güçlendirdi.", author: "Deniz A.", avatarLetter: "D" } ]; const testimonialSliderTrack = document.querySelector('.testimonials-section .testimonials-container') as HTMLElement; const prevTestimonialButton = document.getElementById('prev-testimonial') as HTMLButtonElement; const nextTestimonialButton = document.getElementById('next-testimonial') as HTMLButtonElement; let currentTestimonialIndex = 0; function setupTestimonialSlider() { if (!testimonialSliderTrack) return; testimonialSliderTrack.innerHTML = ''; testimonialsData.forEach(testimonial => { const card = document.createElement('div'); card.className = 'testimonial-card'; const avatar = document.createElement('div'); avatar.className = 'testimonial-avatar'; avatar.textContent = testimonial.avatarLetter || testimonial.author.charAt(0); const text = document.createElement('p'); text.textContent = `"${testimonial.text}"`; const author = document.createElement('div'); author.className = 'author'; author.textContent = testimonial.author; card.appendChild(avatar); card.appendChild(text); card.appendChild(author); testimonialSliderTrack.appendChild(card); }); updateSliderPosition(); updateSliderNavButtons(); } function updateSliderPosition() { if (!testimonialSliderTrack) return; testimonialSliderTrack.style.transform = `translateX(-${currentTestimonialIndex * 100}%)`; } function updateSliderNavButtons() { if (!prevTestimonialButton || !nextTestimonialButton) return; prevTestimonialButton.disabled = currentTestimonialIndex === 0; nextTestimonialButton.disabled = currentTestimonialIndex === testimonialsData.length - 1; } if (prevTestimonialButton && nextTestimonialButton && testimonialSliderTrack) { prevTestimonialButton.addEventListener('click', () => { if (currentTestimonialIndex > 0) { currentTestimonialIndex--; updateSliderPosition(); updateSliderNavButtons(); } }); nextTestimonialButton.addEventListener('click', () => { if (currentTestimonialIndex < testimonialsData.length - 1) { currentTestimonialIndex++; updateSliderPosition(); updateSliderNavButtons(); } }); setupTestimonialSlider(); } });