Implement auto-hiding sticky navbar on scroll and pointer-event safe smooth scroll

This commit is contained in:
Vishva
2026-07-30 10:36:30 +05:30
parent 80ae755825
commit 48f8ab9716
+76 -31
View File
@@ -380,6 +380,30 @@ export default function App({ previewMode = false }) {
const [isCareersOpen, setIsCareersOpen] = useState(false); const [isCareersOpen, setIsCareersOpen] = useState(false);
const [activeSection, setActiveSection] = useState('home'); const [activeSection, setActiveSection] = useState('home');
const [currentPage, setCurrentPage] = useState('home'); const [currentPage, setCurrentPage] = useState('home');
const [navbarVisible, setNavbarVisible] = useState(true);
const lastScrollY = useRef(0);
const smoothScrollTo = (top) => {
document.body.style.pointerEvents = 'none';
window.scrollTo({ top, behavior: 'smooth' });
let scrollTimeout;
const handleScrollEnd = () => {
document.body.style.pointerEvents = '';
window.removeEventListener('scrollend', handleScrollEnd);
window.removeEventListener('scroll', handleScrollDebounce);
clearTimeout(scrollTimeout);
};
const handleScrollDebounce = () => {
clearTimeout(scrollTimeout);
scrollTimeout = setTimeout(handleScrollEnd, 100);
};
window.addEventListener('scrollend', handleScrollEnd);
window.addEventListener('scroll', handleScrollDebounce);
scrollTimeout = setTimeout(handleScrollEnd, 800);
};
useEffect(() => { useEffect(() => {
const syncPageFromHash = () => { const syncPageFromHash = () => {
@@ -387,11 +411,11 @@ export default function App({ previewMode = false }) {
if (hash === '#contact' || hash === '#contactus') { if (hash === '#contact' || hash === '#contactus') {
setCurrentPage('contact'); setCurrentPage('contact');
setActiveSection('contactus'); setActiveSection('contactus');
window.scrollTo({ top: 0, behavior: 'smooth' }); smoothScrollTo(0);
} else if (hash === '#about' || hash === '#aboutus') { } else if (hash === '#about' || hash === '#aboutus') {
setCurrentPage('about'); setCurrentPage('about');
setActiveSection('aboutus'); setActiveSection('aboutus');
window.scrollTo({ top: 0, behavior: 'smooth' }); smoothScrollTo(0);
} else if (hash === '#home' || hash === '') { } else if (hash === '#home' || hash === '') {
setCurrentPage('home'); setCurrentPage('home');
} }
@@ -461,32 +485,49 @@ export default function App({ previewMode = false }) {
}, []); }, []);
useEffect(() => { useEffect(() => {
if (previewMode || currentPage === 'contact') return; if (previewMode) return;
let lastScroll = window.scrollY;
const handleScroll = () => { const handleScroll = () => {
// If at top of the page, home is active const currentScroll = window.scrollY;
if (window.scrollY < 100) { const diff = currentScroll - lastScroll;
setActiveSection('home');
return; if (currentScroll <= 50) {
setNavbarVisible(true);
} else if (diff > 5 && currentScroll > 100) {
setNavbarVisible(false);
} else if (diff < -5) {
setNavbarVisible(true);
} }
const sections = ['home', 'aboutus', 'services', 'whyus', 'products', 'insights', 'contactus']; lastScroll = currentScroll;
const scrollPosition = window.scrollY + 120; // 120px offset for header height and visual center
// Special case: if scrolled down near the very bottom of the page, contactus is active if (currentPage === 'home') {
if (window.scrollY > 300 && window.innerHeight + window.scrollY >= document.documentElement.scrollHeight - 100) { // If at top of the page, home is active
setActiveSection('contactus'); if (currentScroll < 100) {
return; setActiveSection('home');
} return;
}
for (const sectionId of sections) { const sections = ['home', 'aboutus', 'services', 'whyus', 'products', 'insights', 'contactus'];
const el = document.getElementById(sectionId); const scrollPosition = currentScroll + 120; // 120px offset for header height and visual center
if (el) {
const top = el.offsetTop; // Special case: if scrolled down near the very bottom of the page, contactus is active
const height = el.offsetHeight; if (currentScroll > 300 && window.innerHeight + currentScroll >= document.documentElement.scrollHeight - 100) {
if (scrollPosition >= top && scrollPosition < top + height) { setActiveSection('contactus');
setActiveSection(sectionId); return;
break; }
for (const sectionId of sections) {
const el = document.getElementById(sectionId);
if (el) {
const top = el.offsetTop;
const height = el.offsetHeight;
if (scrollPosition >= top && scrollPosition < top + height) {
setActiveSection(sectionId);
break;
}
} }
} }
} }
@@ -495,7 +536,7 @@ export default function App({ previewMode = false }) {
window.addEventListener('scroll', handleScroll); window.addEventListener('scroll', handleScroll);
handleScroll(); // run once on mount handleScroll(); // run once on mount
return () => window.removeEventListener('scroll', handleScroll); return () => window.removeEventListener('scroll', handleScroll);
}, [previewMode]); }, [previewMode, currentPage]);
const getActiveNavItem = () => { const getActiveNavItem = () => {
if (currentPage === 'contact') return 'Contact Us'; if (currentPage === 'contact') return 'Contact Us';
@@ -548,15 +589,16 @@ export default function App({ previewMode = false }) {
const el = document.getElementById(sectionId); const el = document.getElementById(sectionId);
if (el) { if (el) {
const top = el.getBoundingClientRect().top + window.scrollY - NAV_OFFSET; const top = el.getBoundingClientRect().top + window.scrollY - NAV_OFFSET;
window.scrollTo({ top, behavior: 'smooth' }); smoothScrollTo(top);
setActiveSection(sectionId); setActiveSection(sectionId);
} else if (sectionId === 'contactus') { } else if (sectionId === 'contactus') {
window.scrollTo({ top: document.documentElement.scrollHeight, behavior: 'smooth' }); smoothScrollTo(document.documentElement.scrollHeight);
setActiveSection('contactus'); setActiveSection('contactus');
} }
}; };
const handleNavClick = (item) => { const handleNavClick = (item) => {
setNavbarVisible(true);
setMobileMenuOpen(false); setMobileMenuOpen(false);
if (item === 'Careers') { if (item === 'Careers') {
setIsCareersOpen(true); setIsCareersOpen(true);
@@ -567,7 +609,7 @@ export default function App({ previewMode = false }) {
setCurrentPage('contact'); setCurrentPage('contact');
setActiveSection('contactus'); setActiveSection('contactus');
window.location.hash = 'contact'; window.location.hash = 'contact';
window.scrollTo({ top: 0, behavior: 'smooth' }); smoothScrollTo(0);
return; return;
} }
@@ -575,7 +617,7 @@ export default function App({ previewMode = false }) {
setCurrentPage('about'); setCurrentPage('about');
setActiveSection('aboutus'); setActiveSection('aboutus');
window.location.hash = 'aboutus'; window.location.hash = 'aboutus';
window.scrollTo({ top: 0, behavior: 'smooth' }); smoothScrollTo(0);
return; return;
} }
@@ -587,14 +629,14 @@ export default function App({ previewMode = false }) {
const doScroll = (sectionId) => { const doScroll = (sectionId) => {
if (!sectionId || sectionId === 'home') { if (!sectionId || sectionId === 'home') {
window.scrollTo({ top: 0, behavior: 'smooth' }); smoothScrollTo(0);
return; return;
} }
requestAnimationFrame(() => { requestAnimationFrame(() => {
const el = document.getElementById(sectionId); const el = document.getElementById(sectionId);
if (el) { if (el) {
const top = el.getBoundingClientRect().top + window.scrollY - NAV_OFFSET; const top = el.getBoundingClientRect().top + window.scrollY - NAV_OFFSET;
window.scrollTo({ top, behavior: 'smooth' }); smoothScrollTo(top);
setActiveSection(sectionId); setActiveSection(sectionId);
} }
}); });
@@ -695,9 +737,12 @@ export default function App({ previewMode = false }) {
{/* Navigation Bar */} {/* Navigation Bar */}
<nav className="glass site-nav" style={{ <nav className="glass site-nav" style={{
position: 'sticky', top: 0, zIndex: 50, position: 'sticky',
top: navbarVisible ? 0 : (isMobile ? '-72px' : '-88px'),
zIndex: 50,
padding: isMobile ? '0.85rem 1.25rem' : '1.2rem 4rem', padding: isMobile ? '0.85rem 1.25rem' : '1.2rem 4rem',
borderBottom: activeSection === 'products' ? '1px solid rgba(15, 23, 42, 0.08)' : '1px solid rgba(255, 255, 255, 0.05)' borderBottom: activeSection === 'products' ? '1px solid rgba(15, 23, 42, 0.08)' : '1px solid rgba(255, 255, 255, 0.05)',
transition: 'top 0.3s cubic-bezier(0.16, 1, 0.3, 1), border-bottom 0.3s ease, padding 0.3s ease'
}}> }}>
<div style={{ maxWidth: '1440px', margin: '0 auto', width: '100%', display: 'flex', justifyContent: 'space-between', alignItems: 'center' }}> <div style={{ maxWidth: '1440px', margin: '0 auto', width: '100%', display: 'flex', justifyContent: 'space-between', alignItems: 'center' }}>
<div <div