
useState, useEffect, and useRef to manage state, side effects, and direct DOM references.clsx for conditionally applying classes.<header> element with position: fixed.
<header className="fixed top-4 left-4 right-4 md:left-1/2 md:-translate-x-1/2 ... z-50">
<nav className="relative ... rounded-full bg-primary/90 backdrop-blur-sm ...">
{/* Content goes here */}
</nav>
</header>
fixed top-4: This pins the header near the top of the viewport.left-4 right-4: On mobile, it spans most of the width.md:left-1/2 md:-translate-x-1/2: On medium screens and up, it centers itself horizontally.bg-primary/90 backdrop-blur-sm: This creates the semi-transparent, blurred glass effect.rounded-full: This gives it the "pill" shape.useState hook for this:
type ActiveView = 'none' | 'search' | 'menu' | 'readingList';
const [activeView, setActiveView] = useState<ActiveView>('none');
Every interactive element in the header simply calls setActiveView to change what is shown. For example, clicking the search icon sets the view to 'search', which in turn triggers conditional rendering for the search input and results panel.
The component's shape and styles also react to this state. When isSearchOpen is true, the nav element's width expands. When isMenuOpen is true, the border-radius changes from rounded-full to rounded-t-2xl to connect with the dropdown panel below it.
activeView = 'search'.clsx. The main navigation links fade out (opacity-0) while the search input container fades in (opacity-100).useEffect hook triggers when the search view becomes active, automatically focusing the <input> element for a seamless user experience.
const searchInputRef = useRef<HTMLInputElement>(null);
useEffect(() => {
if (isSearchOpen) {
// A short timeout ensures the element is visible before focusing
setTimeout(() => searchInputRef.current?.focus(), 100);
}
}, [isSearchOpen]);
useEffect watches for changes in the search query state. It filters a pre-loaded array of all posts and notes to show relevant results instantly.useEffect hook.
const lastScrollY = useRef(0);
const [isVisible, setIsVisible] = useState(true);
useEffect(() => {
const handleScroll = () => {
const currentScrollY = window.scrollY;
// Show if scrolling up or near the top
if (currentScrollY < lastScrollY.current || currentScrollY < 10) {
setIsVisible(true);
} else if (currentScrollY > 100 && currentScrollY > lastScrollY.current) {
// Hide if scrolling down and past a certain threshold
setIsVisible(false);
}
lastScrollY.current = currentScrollY;
};
window.addEventListener('scroll', handleScroll, { passive: true });
return () => window.removeEventListener('scroll', handleScroll);
}, []);
The isVisible state then adds or removes classes that control the header's opacity and transform.
md:).Loading Conversation...