mirror of
https://github.com/Sea-Haven-Industries/seahaven-site.git
synced 2026-09-30 04:13:15 +00:00
85 lines
2.7 KiB
JavaScript
85 lines
2.7 KiB
JavaScript
|
|
// Shared site chrome behavior: sticky header, accessible mobile menu, hero parallax.
|
||
|
|
(function () {
|
||
|
|
"use strict";
|
||
|
|
|
||
|
|
// ── Sticky header ──────────────────────────────────────────────
|
||
|
|
const header = document.getElementById("site-header");
|
||
|
|
if (header) {
|
||
|
|
const onScroll = () => header.classList.toggle("scrolled", window.scrollY > 40);
|
||
|
|
window.addEventListener("scroll", onScroll, { passive: true });
|
||
|
|
onScroll();
|
||
|
|
}
|
||
|
|
|
||
|
|
// ── Accessible mobile menu (dialog semantics) ──────────────────
|
||
|
|
const hamburger = document.getElementById("hamburger");
|
||
|
|
const mobileMenu = document.getElementById("mobile-menu");
|
||
|
|
const menuClose = document.getElementById("menu-close");
|
||
|
|
|
||
|
|
if (hamburger && mobileMenu && menuClose) {
|
||
|
|
const focusable = () =>
|
||
|
|
mobileMenu.querySelectorAll('a[href], button:not([disabled])');
|
||
|
|
|
||
|
|
const openMenu = () => {
|
||
|
|
mobileMenu.classList.add("open");
|
||
|
|
hamburger.setAttribute("aria-expanded", "true");
|
||
|
|
menuClose.focus();
|
||
|
|
document.addEventListener("keydown", onKeydown);
|
||
|
|
};
|
||
|
|
|
||
|
|
const closeMenu = () => {
|
||
|
|
mobileMenu.classList.remove("open");
|
||
|
|
hamburger.setAttribute("aria-expanded", "false");
|
||
|
|
document.removeEventListener("keydown", onKeydown);
|
||
|
|
hamburger.focus();
|
||
|
|
};
|
||
|
|
|
||
|
|
const onKeydown = (e) => {
|
||
|
|
if (e.key === "Escape") {
|
||
|
|
closeMenu();
|
||
|
|
return;
|
||
|
|
}
|
||
|
|
if (e.key !== "Tab") return;
|
||
|
|
// Focus trap
|
||
|
|
const items = focusable();
|
||
|
|
if (!items.length) return;
|
||
|
|
const first = items[0];
|
||
|
|
const last = items[items.length - 1];
|
||
|
|
if (e.shiftKey && document.activeElement === first) {
|
||
|
|
e.preventDefault();
|
||
|
|
last.focus();
|
||
|
|
} else if (!e.shiftKey && document.activeElement === last) {
|
||
|
|
e.preventDefault();
|
||
|
|
first.focus();
|
||
|
|
}
|
||
|
|
};
|
||
|
|
|
||
|
|
hamburger.addEventListener("click", openMenu);
|
||
|
|
menuClose.addEventListener("click", closeMenu);
|
||
|
|
mobileMenu.querySelectorAll("a").forEach((a) =>
|
||
|
|
a.addEventListener("click", closeMenu)
|
||
|
|
);
|
||
|
|
}
|
||
|
|
|
||
|
|
// ── Hero parallax (homepage only), rAF-throttled ───────────────
|
||
|
|
const heroBg = document.getElementById("hero-bg");
|
||
|
|
if (heroBg) {
|
||
|
|
let ticking = false;
|
||
|
|
const update = () => {
|
||
|
|
if (window.scrollY < window.innerHeight) {
|
||
|
|
heroBg.style.transform = `scale(1.03) translateY(${window.scrollY * 0.18}px)`;
|
||
|
|
}
|
||
|
|
ticking = false;
|
||
|
|
};
|
||
|
|
window.addEventListener(
|
||
|
|
"scroll",
|
||
|
|
() => {
|
||
|
|
if (!ticking) {
|
||
|
|
window.requestAnimationFrame(update);
|
||
|
|
ticking = true;
|
||
|
|
}
|
||
|
|
},
|
||
|
|
{ passive: true }
|
||
|
|
);
|
||
|
|
}
|
||
|
|
})();
|