Fix Neon Mirai active navigation

This commit is contained in:
Paul Bakaus
2026-04-24 23:18:23 -07:00
parent d340f075e8
commit d29a690797
2 changed files with 48 additions and 22 deletions
+4 -4
View File
@@ -9,7 +9,7 @@
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link href="https://fonts.googleapis.com/css2?family=Azeret+Mono:wght@400;500;600;700&family=Chakra+Petch:wght@300;400;500;600&family=Zen+Old+Mincho:wght@400;500;600&display=swap" rel="stylesheet">
<link rel="stylesheet" href="./styles.css?v=20260424-manifesto-regenerated">
<link rel="stylesheet" href="./styles.css?v=20260424-nav-active-fix">
</head>
<body>
<a class="skip-link" href="#main">Skip to content</a>
@@ -31,7 +31,7 @@
</button>
<nav class="site-nav" id="site-nav" aria-label="Primary navigation">
<a href="#agenda" aria-current="page">Home</a>
<a href="#home" aria-current="page">Home</a>
<a href="#agenda">Agenda</a>
<a href="#speakers">Speakers</a>
<a href="#installations">Installations</a>
@@ -48,7 +48,7 @@
</header>
<main id="main">
<section class="hero" aria-labelledby="hero-title">
<section class="hero" id="home" aria-labelledby="hero-title">
<div class="vertical-poem" aria-hidden="true">
<span>未来を描き、共に創る。</span>
<i></i>
@@ -338,6 +338,6 @@
</div>
</footer>
<script src="./script.js?v=20260424-speaker-dots-left"></script>
<script src="./script.js?v=20260424-nav-active-fix"></script>
</body>
</html>
+44 -18
View File
@@ -55,28 +55,54 @@ window.setTimeout(() => {
}, 1600);
const sections = Array.from(document.querySelectorAll("main section[id]"));
let activeNavFrame = 0;
const activeObserver = new IntersectionObserver(
(entries) => {
const visible = entries
.filter((entry) => entry.isIntersecting)
.sort((a, b) => b.intersectionRatio - a.intersectionRatio)[0];
const setActiveNavLink = (sectionId) => {
navLinks.forEach((link) => {
const isActive = link.getAttribute("href") === `#${sectionId}`;
if (isActive) {
link.setAttribute("aria-current", "page");
} else {
link.removeAttribute("aria-current");
}
});
};
if (!visible) return;
const getCurrentSectionId = () => {
const anchorY = (header?.offsetHeight ?? 0) + Math.min(window.innerHeight * 0.32, 260);
let currentSection = sections[0];
navLinks.forEach((link) => {
const isActive = link.getAttribute("href") === `#${visible.target.id}`;
if (isActive) {
link.setAttribute("aria-current", "page");
} else {
link.removeAttribute("aria-current");
}
});
},
{ threshold: [0.24, 0.42, 0.64], rootMargin: "-18% 0px -52% 0px" }
);
for (const section of sections) {
const rect = section.getBoundingClientRect();
if (rect.top <= anchorY && rect.bottom > anchorY) {
return section.id;
}
sections.forEach((section) => activeObserver.observe(section));
if (rect.top <= anchorY) {
currentSection = section;
}
}
return currentSection?.id;
};
const updateActiveNav = () => {
activeNavFrame = 0;
const currentSectionId = getCurrentSectionId();
if (currentSectionId) {
setActiveNavLink(currentSectionId);
}
};
const requestActiveNavUpdate = () => {
if (activeNavFrame) return;
activeNavFrame = window.requestAnimationFrame(updateActiveNav);
};
updateActiveNav();
window.addEventListener("scroll", requestActiveNavUpdate, { passive: true });
window.addEventListener("resize", requestActiveNavUpdate);
window.addEventListener("hashchange", requestActiveNavUpdate);
const speakerCarousel = document.querySelector(".speaker-carousel");