Skip to content
118 changes: 104 additions & 14 deletions src/components/Header.astro
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,6 @@ import links from "@data/links.json";

<section
id="navbar"
class="fixed top-0 z-50 transition-transform duration-300 transform-gpu w-full"
>
<div
class="container max-w-[1150px] mx-auto px-6 py-2 mt-1 lg:p-2 lg:mt-6 flex items-center justify-between relative z-40 bg-white/80 rounded-full backdrop-blur-md shadow-lg"
Expand Down Expand Up @@ -47,28 +46,119 @@ import links from "@data/links.json";
</section>
<Search />

<style>
#navbar {
position: fixed;
top: 0;
width: 100%;
z-index: 1000;
transition: transform 0.3s ease-in-out;
transform: translateY(0);
}
</style>
<script>
document.addEventListener("DOMContentLoaded", () => {
let prevScrollPos = window.pageYOffset;
document.addEventListener("DOMContentLoaded", function() {
const navbar = document.getElementById("navbar") as HTMLElement;
if (!navbar) return;

window.addEventListener("scroll", () => {
let currentScrollPos = window.pageYOffset;
if (prevScrollPos > currentScrollPos) {
let prevScrollPos = window.pageYOffset || document.documentElement.scrollTop;
let ticking = false;
let isVisible = true;

// Ensure navbar is visible initially
navbar.style.transform = "translateY(0)";
navbar.style.transition = "transform 0.3s ease-in-out";

function updateNavbar() {
// Handle iOS bounce - scroll position can be negative during bounce
const rawScrollPos = window.pageYOffset || document.documentElement.scrollTop;
const currentScrollPos = Math.max(0, rawScrollPos);

// Show navbar immediately if we're bouncing above the page (negative scroll)
// or if we're very close to the top
if (rawScrollPos <= 0 || currentScrollPos <= 50) {
if (!isVisible) {
navbar.style.transform = "translateY(0)";
isVisible = true;
}
prevScrollPos = currentScrollPos;
ticking = false;
return;
}

const documentHeight = document.documentElement.scrollHeight;
const windowHeight = window.innerHeight;

// Don't hide navbar when at the very bottom
if (currentScrollPos + windowHeight >= documentHeight - 50) {
if (!isVisible) {
navbar.style.transform = "translateY(0)";
isVisible = true;
}
prevScrollPos = currentScrollPos;
ticking = false;
return;
}

// Only update if scroll difference is significant
const scrollDiff = Math.abs(currentScrollPos - prevScrollPos);
if (scrollDiff < 3) {
ticking = false;
return;
}

// Show navbar when scrolling up, hide when scrolling down
if (prevScrollPos > currentScrollPos && !isVisible) {
navbar.style.transform = "translateY(0)";
} else {
isVisible = true;
} else if (prevScrollPos < currentScrollPos && isVisible && currentScrollPos > 100) {
navbar.style.transform = "translateY(-100%)";
isVisible = false;
}

prevScrollPos = currentScrollPos;
});
ticking = false;
}

navbar.addEventListener("focusin", () => {
navbar.style.transform = "translateY(0)";
});
function requestTick() {
if (!ticking) {
requestAnimationFrame(updateNavbar);
ticking = true;
}
}

navbar.addEventListener("focusout", () => {
if (window.pageYOffset > 100) {
navbar.style.transform = "translateY(-100%)";
// Use passive listener for better performance on mobile
window.addEventListener("scroll", requestTick, { passive: true });

// Additional safety check specifically for iOS bounce recovery
let bounceCheckTimeout: any = null;

window.addEventListener("scroll", function() {
// Clear any existing timeout
if (bounceCheckTimeout) {
clearTimeout(bounceCheckTimeout);
}

// Set a timeout to check position after scroll momentum stops
bounceCheckTimeout = setTimeout(() => {
const finalScrollPos = Math.max(0, window.pageYOffset || document.documentElement.scrollTop);
if (finalScrollPos <= 50 && !isVisible) {
navbar.style.transform = "translateY(0)";
isVisible = true;
prevScrollPos = finalScrollPos;
}
}, 150);
}, { passive: true });

// Handle page visibility changes (when switching tabs/apps)
document.addEventListener("visibilitychange", function() {
if (!document.hidden) {
const currentScrollPos = Math.max(0, window.pageYOffset || document.documentElement.scrollTop);
if (currentScrollPos <= 50) {
navbar.style.transform = "translateY(0)";
isVisible = true;
prevScrollPos = currentScrollPos;
}
}
});
});
Expand Down