Skip to main content

Overview

The Navbar component is a fixed-position navigation bar that provides site navigation, theme switching, and language selection. It features a responsive mobile menu with smooth animations.

Source Location

/src/components/Navbar.astro

Features

  • Fixed position at top of viewport
  • Glass morphism background with backdrop blur
  • Desktop navigation links
  • Collapsible mobile menu
  • Logo with hover animation
  • Integrated ThemeToggle and LanguageSelector
  • Smooth scroll navigation
  • Language-aware routing

Props

No props required. Auto-detects current language from URL.

Code Example

import Navbar from '../components/Navbar.astro';

<Navbar />

Component Structure

<nav id="navbar" class="fixed top-0 left-0 right-0 z-50">
  <div class="bg-white/80 dark:bg-slate-900/80 backdrop-blur-sm ...">
    <div class="max-w-6xl mx-auto px-4 sm:px-6 lg:px-8">
      <div class="flex items-center justify-between h-16">
        
        <!-- Logo -->
        <a href={`/${lang}/#home`}>...</a>

        <!-- Desktop Navigation (hidden on mobile) -->
        <div class="hidden md:flex items-center gap-1">
          <a href={`/${lang}/#home`} class="nav-link">{t('nav.home')}</a>
          <!-- More links -->
        </div>

        <!-- Controls -->
        <div class="flex items-center gap-2">
          <LanguageSelector />
          <ThemeToggle />
          <button id="mobile-menu-btn">...</button>
        </div>
      </div>
    </div>

    <!-- Mobile Menu (collapsible) -->
    <div id="mobile-menu" class="md:hidden ...">
      <!-- Mobile nav links -->
    </div>
  </div>
</nav>
All links are language-aware and use hash navigation:
href={`/${lang}/#home`}
href={`/${lang}/#about`}
href={`/${lang}/#projects`}
href={`/${lang}/#skills`}
href={`/${lang}/#contact`}

Translation Keys

nav.home
string
“Home” navigation link text
nav.about
string
“About” navigation link text
nav.projects
string
“Projects” navigation link text
nav.skills
string
“Skills” navigation link text
nav.contact
string
“Contact” navigation link text

Logo Component

<a href={`/${lang}/#home`} class="flex items-center gap-2 group">
  <div class="w-8 h-8 rounded-lg bg-gradient-to-br from-primary-500 to-accent-500 flex items-center justify-center text-white font-bold text-sm group-hover:scale-110 transition-transform">
    KP
  </div>
  <span class="font-bold text-lg bg-gradient-to-r from-primary-600 to-accent-500 bg-clip-text text-transparent">
    Kevin Palma
  </span>
</a>
  • Gradient background badge with initials
  • Scales on hover (110%)
  • Gradient text for name

Styling Classes

.nav-link {
  @apply px-3 py-2 rounded-lg text-sm font-medium 
         text-slate-600 dark:text-slate-300 
         hover:text-primary-600 dark:hover:text-primary-400 
         hover:bg-primary-50 dark:hover:bg-primary-900/20 
         transition-colors duration-200;
}
.mobile-nav-link {
  @apply block px-3 py-2 rounded-lg text-base font-medium 
         text-slate-600 dark:text-slate-300 
         hover:text-primary-600 dark:hover:text-primary-400 
         hover:bg-primary-50 dark:hover:bg-primary-900/20;
  opacity: 0;
  transform: translateY(-8px);
  transition: opacity 0.25s ease, transform 0.25s ease, 
              background-color 0.2s, color 0.2s;
}
Staggered animation when menu opens:
#mobile-menu.menu-open .mobile-nav-link {
  opacity: 1;
  transform: translateY(0);
}
#mobile-menu.menu-open .mobile-nav-link:nth-child(1) { transition-delay: 50ms; }
#mobile-menu.menu-open .mobile-nav-link:nth-child(2) { transition-delay: 100ms; }
#mobile-menu.menu-open .mobile-nav-link:nth-child(3) { transition-delay: 150ms; }
#mobile-menu.menu-open .mobile-nav-link:nth-child(4) { transition-delay: 200ms; }
#mobile-menu.menu-open .mobile-nav-link:nth-child(5) { transition-delay: 250ms; }

Mobile Menu Script

The component includes JavaScript to control the mobile menu:
const mobileMenuBtn = document.getElementById('mobile-menu-btn');
const mobileMenu = document.getElementById('mobile-menu');
const menuIcon = document.getElementById('menu-icon');
const closeIcon = document.getElementById('close-icon');

function openMenu() {
  if (!mobileMenu) return;
  mobileMenu.classList.remove('max-h-0', 'opacity-0');
  mobileMenu.classList.add('max-h-96', 'opacity-100', 'menu-open');
  menuIcon?.classList.add('hidden');
  closeIcon?.classList.remove('hidden');
}

function closeMenu() {
  if (!mobileMenu) return;
  mobileMenu.classList.remove('max-h-96', 'opacity-100', 'menu-open');
  mobileMenu.classList.add('max-h-0', 'opacity-0');
  menuIcon?.classList.remove('hidden');
  closeIcon?.classList.add('hidden');
}

// Toggle on button click
mobileMenuBtn?.addEventListener('click', () => {
  const isOpen = mobileMenu?.classList.contains('menu-open');
  isOpen ? closeMenu() : openMenu();
});

// Close when clicking a link
mobileMenu?.querySelectorAll('a').forEach(link => {
  link.addEventListener('click', closeMenu);
});

Glass Morphism Background

<div class="bg-white/80 dark:bg-slate-900/80 backdrop-blur-sm border-b border-white/20 dark:border-slate-700/30">
  • 80% opacity background
  • Backdrop blur effect
  • Subtle bottom border
  • Dark mode variant

Responsive Behavior

Desktop (md and up)

  • Logo on left
  • Navigation links in center
  • Controls (Language, Theme, Menu button) on right
  • Mobile menu button hidden

Mobile

  • Logo on left
  • Controls on right (including mobile menu button)
  • Desktop navigation hidden
  • Collapsible mobile menu appears below

Accessibility

  • Fixed positioning: Navbar stays at top (z-index: 50)
  • ARIA label: Menu button has aria-label="Menu"
  • Keyboard navigation: All links and buttons are keyboard accessible
  • Focus management: Mobile menu closes after link click
  • Icon alternatives: Menu and close icons for visual clarity

Performance

  • Backdrop blur uses GPU acceleration
  • CSS-only hover effects
  • Minimal JavaScript (only for mobile menu)
  • Efficient class toggling

Customization

Add to both desktop and mobile sections:
<!-- Desktop -->
<div class="hidden md:flex items-center gap-1">
  <!-- existing links -->
  <a href={`/${lang}/#testimonials`} class="nav-link">{t('nav.testimonials')}</a>
</div>

<!-- Mobile -->
<div id="mobile-menu" class="md:hidden ...">
  <div class="px-4 py-3 space-y-1">
    <!-- existing links -->
    <a href={`/${lang}/#testimonials`} class="mobile-nav-link">{t('nav.testimonials')}</a>
  </div>
</div>
Add translation:
nav: {
  testimonials: 'Testimonials',
}

Changing Background Opacity

<!-- More opaque -->
<div class="bg-white/95 dark:bg-slate-900/95 ...">

<!-- More transparent -->
<div class="bg-white/60 dark:bg-slate-900/60 ...">

Adjusting Height

<!-- Current: 64px (h-16) -->
<div class="flex items-center justify-between h-16">

<!-- Taller: 80px (h-20) -->
<div class="flex items-center justify-between h-20">

Child Components

ThemeToggle

See ThemeToggle component documentation.

LanguageSelector

See LanguageSelector component documentation.

Build docs developers (and LLMs) love