Files
pbakaus_impeccable/public/js/components/section-nav.js
T
Paul Bakaus d85efab51f Refine homepage layout: install, Stay updated, Changelog/FAQ, Visual Mode
- Restructure install section into a split primary card with the
  recommended path on the left and other install methods on the right
- Drop the prefix toggle UI; bundle name is now baked into data-bundle
- Balance the Stay updated step as paired Substack iframe + X follow card
  with matching height, border, and treatment
- Pair Changelog and FAQ side-by-side on desktop with a centered divider
  and shared border-top; section nav highlights both when in view
- Tone down the Visual Mode demo by removing side-tab borders, stacked
  card icons, and tiny body text so the overlay reads cleanly
- Refine the Chrome extension callout: drop the pill badge, use an
  editorial eyebrow above the title, enlarge the thumbnail
- Exclude private evals/ directory from version control
2026-04-07 18:37:08 -07:00

81 lines
2.2 KiB
JavaScript

/**
* Sticky Section Nav
* Shows/hides based on scroll position and highlights current section.
*/
export function initSectionNav() {
const nav = document.getElementById('section-nav');
if (!nav) return;
const items = nav.querySelectorAll('.section-nav-item');
const sectionIds = Array.from(items).map(item => item.dataset.section);
// Show/hide nav based on scroll position
const hero = document.getElementById('hero');
const footer = document.querySelector('.site-footer');
if (!hero) return;
let ticking = false;
function updateNav() {
const scrollY = window.scrollY;
const heroBottom = hero.offsetTop + hero.offsetHeight - 100;
const footerTop = footer ? footer.offsetTop : Infinity;
const viewportBottom = scrollY + window.innerHeight;
// Show nav after hero, hide when footer is visible
if (scrollY > heroBottom && viewportBottom < footerTop + 60) {
nav.classList.add('is-visible');
} else {
nav.classList.remove('is-visible');
}
// Find current section
let currentSection = null;
const viewportMiddle = scrollY + window.innerHeight * 0.4;
for (let i = sectionIds.length - 1; i >= 0; i--) {
const section = document.getElementById(sectionIds[i]);
if (section && section.offsetTop <= viewportMiddle) {
currentSection = sectionIds[i];
break;
}
}
// If the current section shares its top row with siblings (e.g. side-by-side
// changelog + FAQ on desktop), treat all of them as active.
const activeSections = new Set();
if (currentSection) {
const currentEl = document.getElementById(currentSection);
const currentTop = currentEl?.offsetTop ?? 0;
sectionIds.forEach(id => {
const el = document.getElementById(id);
if (el && Math.abs(el.offsetTop - currentTop) < 4) {
activeSections.add(id);
}
});
}
// Update active state
items.forEach(item => {
if (activeSections.has(item.dataset.section)) {
item.classList.add('is-active');
} else {
item.classList.remove('is-active');
}
});
ticking = false;
}
window.addEventListener('scroll', () => {
if (!ticking) {
requestAnimationFrame(updateNav);
ticking = true;
}
}, { passive: true });
// Initial check
updateNav();
}