feat(a11y): ARIA labels, focus management, skip-nav, keyboard nav, contrast fix
All checks were successful
Deploy Feature Branch to Test / deploy (push) Successful in 23s
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
This commit is contained in:
@@ -47,6 +47,7 @@ document.addEventListener("DOMContentLoaded", function () {
|
||||
}
|
||||
|
||||
// Floor accordion
|
||||
// Floor accordion (vanilla JS + a11y)
|
||||
document.querySelectorAll(".floor-header").forEach(function (header) {
|
||||
header.addEventListener("click", function () {
|
||||
var item = this.closest(".floor-item");
|
||||
@@ -56,6 +57,8 @@ document.addEventListener("DOMContentLoaded", function () {
|
||||
// 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";
|
||||
});
|
||||
@@ -63,12 +66,53 @@ document.addEventListener("DOMContentLoaded", function () {
|
||||
// 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 () {
|
||||
@@ -77,6 +121,17 @@ document.addEventListener("DOMContentLoaded", function () {
|
||||
});
|
||||
});
|
||||
|
||||
// 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 () {
|
||||
@@ -85,14 +140,24 @@ document.addEventListener("DOMContentLoaded", function () {
|
||||
});
|
||||
|
||||
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);
|
||||
@@ -103,6 +168,30 @@ document.addEventListener("DOMContentLoaded", function () {
|
||||
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) {
|
||||
|
||||
Reference in New Issue
Block a user