All checks were successful
Deploy Feature Branch to Test / deploy (push) Successful in 23s
Accessibility improvements per WCAG 2.1 AA: - Skip-to-content link (TA-1) - ARIA landmarks and roles for nav, main, sections, footer (TA-2) - Accordion keyboard navigation + aria-expanded (TA-3) - Lightbox focus trap + focus management + dialog role (TA-4) - Gallery grid items keyboard accessible (TA-5) - Improved alt texts for all images (TA-6) - Focus-visible styles for all interactive elements (TA-7) - Darker --stone color for WCAG AA contrast compliance (TA-8) Fix #18
277 lines
8.7 KiB
JavaScript
277 lines
8.7 KiB
JavaScript
document.addEventListener("DOMContentLoaded", function () {
|
||
// Navbar scroll
|
||
var navbar = document.getElementById("navbar");
|
||
window.addEventListener("scroll", function () {
|
||
if (window.scrollY > 60) navbar.classList.add("scrolled");
|
||
else navbar.classList.remove("scrolled");
|
||
});
|
||
|
||
// Hero animation on load
|
||
setTimeout(function () {
|
||
document.getElementById("heroContent").classList.add("visible");
|
||
document.getElementById("heroBg").classList.add("loaded");
|
||
}, 200);
|
||
|
||
// Scroll animations via IntersectionObserver
|
||
var animElements = document.querySelectorAll(".fact, [data-animate]");
|
||
animElements.forEach(function (el) {
|
||
el.style.opacity = "0";
|
||
el.style.transform = "translateY(30px)";
|
||
el.style.transition = "opacity 0.8s ease, transform 0.8s ease";
|
||
});
|
||
|
||
if ("IntersectionObserver" in window) {
|
||
var observer = new IntersectionObserver(
|
||
function (entries) {
|
||
entries.forEach(function (entry) {
|
||
if (entry.isIntersecting) {
|
||
entry.target.classList.add("visible");
|
||
entry.target.style.opacity = "1";
|
||
entry.target.style.transform = "translateY(0)";
|
||
observer.unobserve(entry.target);
|
||
}
|
||
});
|
||
},
|
||
{ rootMargin: "0px 0px -60px 0px" }
|
||
);
|
||
animElements.forEach(function (el) {
|
||
observer.observe(el);
|
||
});
|
||
} else {
|
||
// Fallback: show all immediately
|
||
animElements.forEach(function (el) {
|
||
el.classList.add("visible");
|
||
el.style.opacity = "1";
|
||
el.style.transform = "translateY(0)";
|
||
});
|
||
}
|
||
|
||
// Floor accordion
|
||
// Floor accordion (vanilla JS + a11y)
|
||
document.querySelectorAll(".floor-header").forEach(function (header) {
|
||
header.addEventListener("click", function () {
|
||
var item = this.closest(".floor-item");
|
||
var isOpen = item.classList.contains("open");
|
||
var allItems = document.querySelectorAll(".floor-item");
|
||
|
||
// Close all
|
||
allItems.forEach(function (fi) {
|
||
fi.classList.remove("open");
|
||
var hdr = fi.querySelector(".floor-header");
|
||
if (hdr) hdr.setAttribute("aria-expanded", "false");
|
||
var body = fi.querySelector(".floor-body");
|
||
if (body) body.style.display = "none";
|
||
});
|
||
|
||
// Open clicked if it was closed
|
||
if (!isOpen) {
|
||
item.classList.add("open");
|
||
this.setAttribute("aria-expanded", "true");
|
||
var body = item.querySelector(".floor-body");
|
||
if (body) body.style.display = "block";
|
||
}
|
||
});
|
||
});
|
||
|
||
// Accordion keyboard handler (Enter/Space)
|
||
document.querySelectorAll(".floor-header").forEach(function (header) {
|
||
header.addEventListener("keydown", function (e) {
|
||
if (e.key === "Enter" || e.key === " ") {
|
||
e.preventDefault();
|
||
this.click();
|
||
}
|
||
});
|
||
if (e.key === "Enter" || e.key === " ") {
|
||
e.preventDefault();
|
||
$(this).trigger("click");
|
||
}
|
||
});
|
||
|
||
// Lightbox – track last focused element for focus return
|
||
var lightboxTrigger = null;
|
||
|
||
function openLightbox(src) {
|
||
lightboxTrigger = document.activeElement;
|
||
$("#lightboxImg").attr("src", src).attr("alt", "");
|
||
$("#lightbox").addClass("open");
|
||
$("body").css("overflow", "hidden");
|
||
// Set focus to close button
|
||
setTimeout(function () {
|
||
$("#lightboxClose").focus();
|
||
}, 50);
|
||
}
|
||
|
||
function closeLightbox() {
|
||
$("#lightbox").removeClass("open");
|
||
$("body").css("overflow", "");
|
||
// Return focus to trigger
|
||
if (lightboxTrigger) {
|
||
$(lightboxTrigger).focus();
|
||
lightboxTrigger = null;
|
||
}
|
||
}
|
||
|
||
// Lightbox – gallery grid items
|
||
|
||
// Lightbox – gallery grid items
|
||
document.querySelectorAll(".grid-item").forEach(function (item) {
|
||
item.addEventListener("click", function () {
|
||
var src = this.dataset.img || this.querySelector("img").getAttribute("src");
|
||
openLightbox(src);
|
||
});
|
||
});
|
||
|
||
// Gallery keyboard handler (Enter/Space)
|
||
document.querySelectorAll(".grid-item").forEach(function (item) {
|
||
item.setAttribute("tabindex", "0");
|
||
item.addEventListener("keydown", function (e) {
|
||
if (e.key === "Enter" || e.key === " ") {
|
||
e.preventDefault();
|
||
this.click();
|
||
}
|
||
});
|
||
});
|
||
|
||
// Lightbox – floor plan images in Raumaufteilung
|
||
document.querySelectorAll(".floor-plan img[data-img]").forEach(function (img) {
|
||
img.addEventListener("click", function () {
|
||
openLightbox(this.dataset.img);
|
||
});
|
||
});
|
||
|
||
function openLightbox(src) {
|
||
lightboxTrigger = document.activeElement;
|
||
document.getElementById("lightboxImg").setAttribute("src", src);
|
||
document.getElementById("lightbox").classList.add("open");
|
||
document.body.style.overflow = "hidden";
|
||
// Focus close button
|
||
setTimeout(function () {
|
||
document.getElementById("lightboxClose").focus();
|
||
}, 100);
|
||
}
|
||
|
||
function closeLightbox() {
|
||
document.getElementById("lightbox").classList.remove("open");
|
||
document.body.style.overflow = "";
|
||
// Return focus to trigger
|
||
if (lightboxTrigger) {
|
||
lightboxTrigger.focus();
|
||
lightboxTrigger = null;
|
||
}
|
||
}
|
||
|
||
document.getElementById("lightboxClose").addEventListener("click", closeLightbox);
|
||
document.getElementById("lightbox").addEventListener("click", function (e) {
|
||
if (e.target === this) closeLightbox();
|
||
});
|
||
document.addEventListener("keydown", function (e) {
|
||
if (e.key === "Escape") closeLightbox();
|
||
});
|
||
|
||
// Focus trap for lightbox
|
||
document.getElementById("lightbox").addEventListener("keydown", function (e) {
|
||
if (e.key !== "Tab") return;
|
||
|
||
var focusable = this.querySelectorAll("button, [href], input, select, textarea, [tabindex]:not([tabindex='-1'])");
|
||
focusable = Array.from(focusable).filter(function (el) { return el.offsetParent !== null; });
|
||
if (focusable.length === 0) return;
|
||
|
||
var first = focusable[0];
|
||
var last = focusable[focusable.length - 1];
|
||
|
||
if (e.shiftKey) {
|
||
if (document.activeElement === first) {
|
||
e.preventDefault();
|
||
last.focus();
|
||
}
|
||
} else {
|
||
if (document.activeElement === last) {
|
||
e.preventDefault();
|
||
first.focus();
|
||
}
|
||
}
|
||
});
|
||
|
||
// Form submit is handled server-side by PHP – no JS intervention needed.
|
||
// Form submit – opens email client with pre-filled mailto: link
|
||
document.getElementById("contactForm").addEventListener("submit", function (e) {
|
||
e.preventDefault();
|
||
|
||
var fname = document.getElementById("fname").value.trim();
|
||
var lname = document.getElementById("lname").value.trim();
|
||
var email = document.getElementById("email").value.trim();
|
||
var phone = document.getElementById("phone").value.trim();
|
||
var interest = document.getElementById("interest").value;
|
||
var message = document.getElementById("message").value.trim();
|
||
|
||
var subject = "Kontaktanfrage: " + interest;
|
||
var body = "Von: " + fname + " " + lname + "\n";
|
||
body += "E-Mail: " + email + "\n";
|
||
if (phone) body += "Telefon: " + phone + "\n";
|
||
body += "Anliegen: " + interest + "\n\n";
|
||
body += message;
|
||
|
||
var mailto =
|
||
"mailto:mki@kies-media.de" +
|
||
"?subject=" + encodeURIComponent(subject) +
|
||
"&body=" + encodeURIComponent(body);
|
||
|
||
window.location.href = mailto;
|
||
|
||
// Show success message
|
||
this.style.display = "none";
|
||
var success = document.getElementById("formSuccess");
|
||
success.style.display = "block";
|
||
success.style.opacity = "0";
|
||
success.style.transition = "opacity 0.4s ease";
|
||
requestAnimationFrame(function () {
|
||
success.style.opacity = "1";
|
||
});
|
||
});
|
||
});
|
||
|
||
// Mobile hamburger menu (vanilla JS)
|
||
(function () {
|
||
var hamburger = document.querySelector(".nav-hamburger");
|
||
var nav = document.getElementById("navbar");
|
||
var overlay = document.querySelector(".nav-mobile-overlay");
|
||
var links = nav ? nav.querySelectorAll(".nav-links a") : [];
|
||
|
||
function toggleMenu() {
|
||
var isOpen = hamburger.classList.toggle("active");
|
||
nav.classList.toggle("mobile-open", isOpen);
|
||
if (overlay) overlay.classList.toggle("active", isOpen);
|
||
hamburger.setAttribute("aria-expanded", isOpen);
|
||
document.body.style.overflow = isOpen ? "hidden" : "";
|
||
}
|
||
|
||
function closeMenu() {
|
||
hamburger.classList.remove("active");
|
||
nav.classList.remove("mobile-open");
|
||
if (overlay) overlay.classList.remove("active");
|
||
hamburger.setAttribute("aria-expanded", "false");
|
||
document.body.style.overflow = "";
|
||
}
|
||
|
||
if (hamburger) {
|
||
hamburger.addEventListener("click", toggleMenu);
|
||
}
|
||
|
||
if (overlay) {
|
||
overlay.addEventListener("click", closeMenu);
|
||
}
|
||
|
||
links.forEach(function (link) {
|
||
link.addEventListener("click", closeMenu);
|
||
});
|
||
|
||
document.addEventListener("keydown", function (e) {
|
||
if (e.key === "Escape") closeMenu();
|
||
});
|
||
|
||
// Close on resize to desktop
|
||
window.addEventListener("resize", function () {
|
||
if (window.innerWidth > 900) closeMenu();
|
||
});
|
||
})();
|