Kompletter Relaunch als statische Website: - index.html: One-Pager mit Hero, Leistungen, USPs, Team, Kontakt - impressum.html + datenschutz.html: Separate Unterseiten - TailwindCSS via CDN, Custom Styles, Scroll-Animationen - Mobile Menu, Scroll-Spy, Fade-in Animationen - Farbschema: Dunkel-modern mit Sonith-Gelb (#DEDC1B) Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
97 lines
2.9 KiB
JavaScript
97 lines
2.9 KiB
JavaScript
/* ============================================================
|
|
sonith.de — Main JavaScript
|
|
============================================================ */
|
|
|
|
document.addEventListener('DOMContentLoaded', () => {
|
|
initMobileMenu();
|
|
initScrollSpy();
|
|
initNavbarScroll();
|
|
initFadeIn();
|
|
});
|
|
|
|
/* ---- Mobile Menu ---- */
|
|
function initMobileMenu() {
|
|
const btn = document.getElementById('mobile-menu-btn');
|
|
const menu = document.getElementById('mobile-menu');
|
|
const iconOpen = document.getElementById('menu-icon-open');
|
|
const iconClose = document.getElementById('menu-icon-close');
|
|
|
|
if (!btn || !menu) return;
|
|
|
|
btn.addEventListener('click', () => {
|
|
const isOpen = !menu.classList.contains('hidden');
|
|
menu.classList.toggle('hidden');
|
|
iconOpen.classList.toggle('hidden');
|
|
iconClose.classList.toggle('hidden');
|
|
btn.setAttribute('aria-label', isOpen ? 'Menü öffnen' : 'Menü schließen');
|
|
});
|
|
|
|
// Menü schließen bei Klick auf Link
|
|
menu.querySelectorAll('a').forEach(link => {
|
|
link.addEventListener('click', () => {
|
|
menu.classList.add('hidden');
|
|
iconOpen.classList.remove('hidden');
|
|
iconClose.classList.add('hidden');
|
|
btn.setAttribute('aria-label', 'Menü öffnen');
|
|
});
|
|
});
|
|
}
|
|
|
|
/* ---- Scroll Spy (aktiver Menüpunkt) ---- */
|
|
function initScrollSpy() {
|
|
const sections = document.querySelectorAll('section[id]');
|
|
const navLinks = document.querySelectorAll('.nav-link');
|
|
|
|
if (!sections.length || !navLinks.length) return;
|
|
|
|
const observer = new IntersectionObserver(entries => {
|
|
entries.forEach(entry => {
|
|
if (entry.isIntersecting) {
|
|
const id = entry.target.getAttribute('id');
|
|
navLinks.forEach(link => {
|
|
link.classList.toggle('active', link.getAttribute('href') === `#${id}`);
|
|
});
|
|
}
|
|
});
|
|
}, {
|
|
rootMargin: '-20% 0px -60% 0px',
|
|
threshold: 0
|
|
});
|
|
|
|
sections.forEach(section => observer.observe(section));
|
|
}
|
|
|
|
/* ---- Navbar Background on Scroll ---- */
|
|
function initNavbarScroll() {
|
|
const navbar = document.getElementById('navbar');
|
|
if (!navbar) return;
|
|
|
|
const update = () => {
|
|
navbar.classList.toggle('scrolled', window.scrollY > 50);
|
|
};
|
|
|
|
window.addEventListener('scroll', update, { passive: true });
|
|
update();
|
|
}
|
|
|
|
/* ---- Fade-in on Scroll ---- */
|
|
function initFadeIn() {
|
|
const elements = document.querySelectorAll('.fade-in, .service-card');
|
|
|
|
if (!elements.length) return;
|
|
|
|
const observer = new IntersectionObserver(entries => {
|
|
entries.forEach(entry => {
|
|
if (entry.isIntersecting) {
|
|
entry.target.classList.add('visible');
|
|
observer.unobserve(entry.target);
|
|
}
|
|
});
|
|
}, {
|
|
threshold: 0.1,
|
|
rootMargin: '0px 0px -40px 0px'
|
|
});
|
|
|
|
elements.forEach(el => observer.observe(el));
|
|
}
|