Files
blowfish/assets/js/menu-a11y.js
T
Nuno CoraçãoandClaude Fable 5 09f0573749 ✨ v3 theme: glass UI, landing layout, a11y, perf, print styles
Floating glass header with ⌘K search, landing homepage layout with
hero image and GitHub-stars CTA, background canvas with dissolving
heroes, burufugu colour scheme, movie-quote 404, taxonomy pills,
keyboard/ARIA accessibility across menus, search and tabs, idle-
loaded third-party scripts, and dedicated print styles.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-17 20:19:02 +01:00

90 lines
2.8 KiB
JavaScript

(function () {
"use strict";
function syncCheckboxLabels(checkbox) {
var expanded = checkbox.checked ? "true" : "false";
document.querySelectorAll('label[role="button"][for="' + checkbox.id + '"]').forEach(function (label) {
label.setAttribute("aria-expanded", expanded);
});
}
function setupCheckboxLabels() {
var seen = {};
document.querySelectorAll('label[role="button"][for]').forEach(function (label) {
var checkbox = document.getElementById(label.getAttribute("for"));
if (!checkbox || checkbox.type !== "checkbox") return;
// Enter/Space activates the label like a real button
label.addEventListener("keydown", function (event) {
if (event.key === "Enter" || event.key === " " || event.key === "Spacebar") {
event.preventDefault();
label.click();
}
});
if (!seen[checkbox.id]) {
seen[checkbox.id] = true;
checkbox.addEventListener("change", function () {
syncCheckboxLabels(checkbox);
});
syncCheckboxLabels(checkbox);
}
});
}
function setupMobileMenuEscape() {
var toggle = document.getElementById("mobile-menu-toggle");
if (!toggle) return;
document.addEventListener("keydown", function (event) {
if (event.key === "Escape" && toggle.checked) {
toggle.checked = false;
syncCheckboxLabels(toggle);
var opener = document.querySelector('label[role="button"][for="mobile-menu-toggle"]');
if (opener) opener.focus();
}
});
}
function setupDesktopDropdowns() {
document.querySelectorAll(".nested-menu").forEach(function (menu) {
var trigger = menu.querySelector("[aria-haspopup]");
if (!trigger) return;
var setExpanded = function (expanded) {
trigger.setAttribute("aria-expanded", expanded ? "true" : "false");
};
menu.addEventListener("mouseenter", function () {
setExpanded(true);
});
menu.addEventListener("mouseleave", function () {
setExpanded(false);
});
menu.addEventListener("focusin", function () {
setExpanded(true);
});
menu.addEventListener("focusout", function (event) {
if (!menu.contains(event.relatedTarget)) setExpanded(false);
});
menu.addEventListener("keydown", function (event) {
if (event.key === "Escape") {
if (document.activeElement && menu.contains(document.activeElement)) {
document.activeElement.blur();
}
setExpanded(false);
}
});
});
}
function init() {
setupCheckboxLabels();
setupMobileMenuEscape();
setupDesktopDropdowns();
}
if (document.readyState === "loading") {
document.addEventListener("DOMContentLoaded", init);
} else {
init();
}
})();