Files
Tiber365/src/components/Header.astro

259 lines
10 KiB
Plaintext

---
import { NAVIGATION } from '../site.config';
import ThemeToggle from './ThemeToggle.astro';
import LanguageSwitcher from './LanguageSwitcher.astro';
import { getLangFromUrl, useTranslations, localizePath } from '../utils/i18n';
const lang = getLangFromUrl(Astro.url);
const t = await useTranslations(lang);
---
<header class="fixed top-0 left-0 right-0 z-50 transition-all duration-500" id="main-header">
<!-- Glass morphism navigation -->
<nav class="nav-glass">
<div class="container-custom">
<div class="flex h-20 items-center justify-between">
<!-- Logo with modern styling -->
<div class="flex items-center">
<a href={localizePath("/", lang)} class="group flex items-center space-x-3 transition-all">
<div class="relative">
<div class="absolute inset-0 bg-primary/20 rounded-xl blur-md opacity-0 group-hover:opacity-100 transition-opacity"></div>
<div class="relative h-10 w-10 bg-gradient-to-br from-primary to-accent rounded-xl flex items-center justify-center">
<img
src="/images/TIBER365.png"
alt="Tiber365 Logo"
class="h-6 w-6 object-contain brightness-0 invert"
/>
</div>
</div>
<div class="flex flex-col">
<span class="font-bold text-lg text-foreground group-hover:text-primary transition-colors">Tiber365</span>
<span class="text-xs text-subtle -mt-1">IT Excellence</span>
</div>
</a>
</div>
<!-- Desktop Navigation with modern hover effects -->
<div class="hidden lg:flex items-center space-x-1">
{NAVIGATION.map((item) => (
<a
href={item.type === 'external' ? item.href : localizePath(item.href, lang)}
target={item.type === 'external' ? '_blank' : undefined}
rel={item.type === 'external' ? 'noopener noreferrer' : undefined}
class="relative px-4 py-2 text-sm font-medium text-muted hover:text-foreground transition-all duration-300 rounded-lg group"
>
<span class="relative z-10 flex items-center gap-1">
{t(item.label)}
{item.type === 'external' && (
<svg class="h-3 w-3 opacity-60" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M10 6H6a2 2 0 00-2 2v10a2 2 0 002 2h10a2 2 0 002-2v-4M14 4h6m0 0v6m0-6L10 14"/>
</svg>
)}
</span>
<!-- Hover background -->
<div class="absolute inset-0 bg-surface/50 rounded-lg opacity-0 group-hover:opacity-100 transition-all duration-300 scale-95 group-hover:scale-100"></div>
<!-- Active indicator -->
<div class="absolute bottom-0 left-1/2 transform -translate-x-1/2 w-0 h-0.5 bg-gradient-to-r from-primary to-accent group-hover:w-full transition-all duration-300"></div>
</a>
))}
<!-- CTA Button in nav -->
<a
href={localizePath("/contact", lang)}
class="ml-4 btn btn-primary btn-sm"
>
Get Started
</a>
</div>
<!-- Right side controls -->
<div class="flex items-center space-x-3">
<!-- Language switcher with modern styling -->
<LanguageSwitcher />
<!-- Theme toggle with modern animation -->
<ThemeToggle />
<!-- Mobile Menu Button -->
<button
id="mobile-menu-button"
class="lg:hidden p-2 rounded-xl bg-surface/50 border border-border/50 text-muted hover:text-foreground hover:bg-surface transition-all"
aria-expanded="false"
aria-label="Toggle mobile menu"
>
<svg id="mobile-menu-icon" class="h-5 w-5 transition-transform" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M4 6h16M4 12h16M4 18h16"/>
</svg>
<svg id="mobile-close-icon" class="h-5 w-5 hidden transition-transform" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M6 18L18 6M6 6l12 12"/>
</svg>
</button>
</div>
</div>
</div>
<!-- Mobile Navigation Menu -->
<div id="mobile-menu" class="lg:hidden hidden border-t border-border/50">
<div class="container-custom py-6">
<div class="space-y-2">
{NAVIGATION.map((item) => (
<a
href={item.type === 'external' ? item.href : localizePath(item.href, lang)}
target={item.type === 'external' ? '_blank' : undefined}
rel={item.type === 'external' ? 'noopener noreferrer' : undefined}
class="flex items-center justify-between p-4 text-base font-medium text-muted hover:text-foreground hover:bg-surface/50 rounded-xl transition-all group"
>
<span class="flex items-center gap-2">
{t(item.label)}
{item.type === 'external' && (
<svg class="h-4 w-4 opacity-60" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M10 6H6a2 2 0 00-2 2v10a2 2 0 002 2h10a2 2 0 002-2v-4M14 4h6m0 0v6m0-6L10 14"/>
</svg>
)}
</span>
<svg class="h-4 w-4 opacity-40 group-hover:opacity-60 transition-all" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 5l7 7-7 7"/>
</svg>
</a>
))}
<!-- Mobile CTA -->
<div class="pt-4 mt-4 border-t border-border/50">
<a
href={localizePath("/contact", lang)}
class="btn btn-primary w-full justify-center"
>
Get Started Today
</a>
</div>
</div>
</div>
</div>
</nav>
<!-- Progress bar for scroll -->
<div class="absolute bottom-0 left-0 h-0.5 bg-gradient-to-r from-primary to-accent transition-all duration-300" id="scroll-progress"></div>
</header>
<script>
// Enhanced mobile menu functionality with animations
document.addEventListener('DOMContentLoaded', () => {
const header = document.getElementById('main-header');
const mobileMenuButton = document.getElementById('mobile-menu-button');
const mobileMenu = document.getElementById('mobile-menu');
const menuIcon = document.getElementById('mobile-menu-icon');
const closeIcon = document.getElementById('mobile-close-icon');
const scrollProgress = document.getElementById('scroll-progress');
// Header scroll effect
let lastScrollY = window.scrollY;
function updateHeader() {
const currentScrollY = window.scrollY;
if (currentScrollY > 100) {
header?.classList.add('backdrop-blur-xl', 'bg-background/80');
header?.classList.remove('bg-transparent');
} else {
header?.classList.remove('backdrop-blur-xl', 'bg-background/80');
header?.classList.add('bg-transparent');
}
// Hide/show header on scroll
if (currentScrollY > lastScrollY && currentScrollY > 100) {
header?.classList.add('-translate-y-full');
} else {
header?.classList.remove('-translate-y-full');
}
lastScrollY = currentScrollY;
// Update progress bar
const scrollPercentage = (currentScrollY / (document.documentElement.scrollHeight - window.innerHeight)) * 100;
if (scrollProgress) {
scrollProgress.style.width = `${Math.min(scrollPercentage, 100)}%`;
}
}
window.addEventListener('scroll', updateHeader, { passive: true });
// Mobile menu toggle with enhanced animations
if (mobileMenuButton && mobileMenu && menuIcon && closeIcon) {
mobileMenuButton.addEventListener('click', () => {
const isExpanded = mobileMenuButton.getAttribute('aria-expanded') === 'true';
const newState = !isExpanded;
mobileMenuButton.setAttribute('aria-expanded', newState.toString());
if (newState) {
mobileMenu.classList.remove('hidden');
// Animate in
requestAnimationFrame(() => {
mobileMenu.style.opacity = '0';
mobileMenu.style.transform = 'translateY(-10px)';
mobileMenu.style.transition = 'all 0.3s cubic-bezier(0.4, 0, 0.2, 1)';
requestAnimationFrame(() => {
mobileMenu.style.opacity = '1';
mobileMenu.style.transform = 'translateY(0)';
});
});
menuIcon.classList.add('hidden');
closeIcon.classList.remove('hidden');
document.body.style.overflow = 'hidden';
} else {
// Animate out
mobileMenu.style.opacity = '0';
mobileMenu.style.transform = 'translateY(-10px)';
setTimeout(() => {
mobileMenu.classList.add('hidden');
document.body.style.overflow = '';
}, 300);
menuIcon.classList.remove('hidden');
closeIcon.classList.add('hidden');
}
});
// Close menu when clicking outside
document.addEventListener('click', (event) => {
if (!mobileMenuButton.contains(event.target as Node) &&
!mobileMenu.contains(event.target as Node) &&
!mobileMenu.classList.contains('hidden')) {
mobileMenuButton.click();
}
});
// Close menu on escape key
document.addEventListener('keydown', (event) => {
if (event.key === 'Escape' && !mobileMenu.classList.contains('hidden')) {
mobileMenuButton.click();
}
});
}
});
</script>
<style>
/* Enhanced mobile menu animations */
#mobile-menu {
backdrop-filter: blur(20px);
-webkit-backdrop-filter: blur(20px);
}
/* Smooth header transitions */
#main-header {
transition: transform 0.3s cubic-bezier(0.4, 0, 0.2, 1),
background-color 0.3s cubic-bezier(0.4, 0, 0.2, 1),
backdrop-filter 0.3s cubic-bezier(0.4, 0, 0.2, 1);
}
/* Logo hover effect */
.group:hover .brightness-0 {
filter: brightness(1) invert(0);
transition: filter 0.3s ease;
}
</style>