Skip to main content

Documentation Index

Fetch the complete documentation index at: https://mintlify.com/nicolasgrajaleshoyos/portafolio/llms.txt

Use this file to discover all available pages before exploring further.

The Header component sits at the top of every page as a position: fixed bar, ensuring navigation is always reachable while the user scrolls through the single-page portfolio. It accepts the current theme and a toggle callback from App.tsx, owns its own scroll-detection state to apply a glassmorphism background, and manages a mobile hamburger menu with a matching full-screen nav drawer.

Props

theme
'light' | 'dark'
required
The active colour theme, managed by the useTheme hook in App.tsx. Used by the internal ThemeToggle sub-component to decide which icon to render — MoonIcon in 'light' mode and SunIcon in 'dark' mode.
toggleTheme
() => void
required
A callback that flips the theme between 'light' and 'dark'. Triggered when the user clicks the theme-toggle button. The function is defined in App.tsx by the useTheme custom hook and persists the preference to localStorage.

Behavior

Scroll-based glassmorphism

The component tracks window.scrollY through a scroll event listener mounted in a useEffect. When the page is scrolled more than 10 px, isScrolled is set to true, which swaps the header background from fully transparent to:
bg-white/80 shadow-md backdrop-blur-md dark:bg-dark/80
The listener is cleaned up on unmount to prevent memory leaks.

Mobile menu

On small screens (below the md breakpoint) a hamburger button toggles isMenuOpen. When open, a stacked nav drawer appears below the header bar, styled with bg-white dark:bg-dark-secondary shadow-lg. Clicking any nav link calls closeMenu() to collapse the drawer automatically.

Smooth-scroll navigation (handleNavClick)

Every nav link uses an onClick handler that:
  1. Calls e.preventDefault() to stop the default anchor jump.
  2. Reads the href attribute (#home, #about, #projects, or #contact).
  3. Finds the target element via document.querySelector(targetId).
  4. Applies a 64 px offset (equal to h-16, the fixed header height) so the section headline is never hidden behind the bar.
  5. Calls window.scrollTo({ top: offsetPosition, behavior: 'smooth' }).
An internal NavLink wraps each anchor and adds a sliding underline accent on hover:
<span className="absolute bottom-0 left-0 w-full h-0.5 bg-primary scale-x-0 group-hover:scale-x-100 transition-transform origin-center duration-300 ease-out" />
The origin-center class makes the line grow symmetrically from the middle outward.

ThemeToggle sub-component

Renders a circular icon button. When theme === 'light', it shows MoonIcon (inviting the user to switch to dark mode); when theme === 'dark', it shows SunIcon. The button carries an aria-label="Toggle theme" for screen-reader accessibility.
LabelTarget ID
Inicio#home
Sobre mí#about
Proyectos#projects
Contacto#contact

Usage Example

The Header is mounted at the top of App.tsx, receiving theme and toggleTheme from the useTheme custom hook:
import Header from '@/components/Header';

const App: React.FC = () => {
  const [theme, toggleTheme] = useTheme();

  return (
    <div className="flex flex-col min-h-screen">
      <Header theme={theme} toggleTheme={toggleTheme} />
      <main className="flex-grow">
        {/* page sections */}
      </main>
    </div>
  );
};

Customization

The glassmorphism effect triggers at 10 px. To change it, edit the condition inside the handleScroll function:
setIsScrolled(window.scrollY > 50); // trigger later
The logo is an anchor tag near the top of the JSX return. Replace the inner text to rebrand:
<a href="#home" onClick={handleNavClick} className="...">
  Your Name<span className="text-primary">.</span>
</a>
If you change the header height from h-16 to something larger, update the matching pixel constant in handleNavClick:
const headerOffset = 80; // matches h-20 (5rem)

Build docs developers (and LLMs) love