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>
Navigation Links
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
“Home” navigation link text
“About” navigation link text
“Projects” navigation link text
“Skills” navigation link text
“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
Desktop Navigation Links
.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 Navigation Links
.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; }
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
- Backdrop blur uses GPU acceleration
- CSS-only hover effects
- Minimal JavaScript (only for mobile menu)
- Efficient class toggling
Customization
Adding Navigation Links
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.