439 lines
12 KiB
Plaintext
439 lines
12 KiB
Plaintext
---
|
|
// Header.astro
|
|
// Primary navigation component with premium design elements
|
|
|
|
// Get current path to highlight active nav item
|
|
const pathname = new URL(Astro.request.url).pathname;
|
|
const currentPath = pathname.split('/')[1]; // Get the first path segment
|
|
|
|
// Define navigation items
|
|
const navItems = [
|
|
{ name: 'Home', path: '/' },
|
|
{ name: 'Blog', path: '/blog/' },
|
|
{ name: 'Projects', path: '/projects/' },
|
|
{ name: 'Home Lab', path: '/homelab/' },
|
|
{ name: 'Resources', path: '/resources/' },
|
|
{ name: 'About', path: '/about/' },
|
|
{ name: 'Contact', path: '/contact/' }
|
|
];
|
|
---
|
|
|
|
<header class="site-header">
|
|
<div class="nebula-bg"></div>
|
|
<div class="container">
|
|
<div class="header-container">
|
|
<a href="/" class="logo">
|
|
<div class="logo-symbol">
|
|
<span class="logo-text">LF</span>
|
|
<div class="logo-glow"></div>
|
|
</div>
|
|
<div class="logo-text-container">
|
|
<span class="logo-name">LaForceIT</span>
|
|
<span class="logo-tagline">Infrastructure & Automation</span>
|
|
</div>
|
|
</a>
|
|
|
|
<nav>
|
|
<div class="main-nav">
|
|
{navItems.map((item) => (
|
|
<a
|
|
href={item.path}
|
|
class={`nav-link ${currentPath === item.path.replace(/\//g, '') ? 'active' : ''}`}
|
|
>
|
|
{item.name}
|
|
<div class="nav-highlight"></div>
|
|
</a>
|
|
))}
|
|
</div>
|
|
|
|
<div class="header-actions">
|
|
<button id="theme-toggle" class="theme-toggle" aria-label="Toggle dark mode">
|
|
<svg class="icon-sun" xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
|
|
<circle cx="12" cy="12" r="5"></circle>
|
|
<line x1="12" y1="1" x2="12" y2="3"></line>
|
|
<line x1="12" y1="21" x2="12" y2="23"></line>
|
|
<line x1="4.22" y1="4.22" x2="5.64" y2="5.64"></line>
|
|
<line x1="18.36" y1="18.36" x2="19.78" y2="19.78"></line>
|
|
<line x1="1" y1="12" x2="3" y2="12"></line>
|
|
<line x1="21" y1="12" x2="23" y2="12"></line>
|
|
<line x1="4.22" y1="19.78" x2="5.64" y2="18.36"></line>
|
|
<line x1="18.36" y1="5.64" x2="19.78" y2="4.22"></line>
|
|
</svg>
|
|
<svg class="icon-moon" xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
|
|
<path d="M21 12.79A9 9 0 1 1 11.21 3 7 7 0 0 0 21 12.79z"></path>
|
|
</svg>
|
|
</button>
|
|
|
|
<button id="search-button" class="search-button" aria-label="Search">
|
|
<svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
|
|
<circle cx="11" cy="11" r="8"></circle>
|
|
<line x1="21" y1="21" x2="16.65" y2="16.65"></line>
|
|
</svg>
|
|
</button>
|
|
|
|
<button id="mobile-menu-btn" class="mobile-menu-btn" aria-label="Toggle menu">
|
|
<svg class="icon-menu" xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
|
|
<line x1="3" y1="12" x2="21" y2="12"></line>
|
|
<line x1="3" y1="6" x2="21" y2="6"></line>
|
|
<line x1="3" y1="18" x2="21" y2="18"></line>
|
|
</svg>
|
|
<svg class="icon-close" xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
|
|
<line x1="18" y1="6" x2="6" y2="18"></line>
|
|
<line x1="6" y1="6" x2="18" y2="18"></line>
|
|
</svg>
|
|
</button>
|
|
</div>
|
|
</nav>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Animated network lines effect -->
|
|
<div class="network-lines"></div>
|
|
</header>
|
|
|
|
<style>
|
|
.site-header {
|
|
background: linear-gradient(180deg, rgba(15, 18, 25, 0.9), rgba(13, 16, 23, 0.8));
|
|
backdrop-filter: blur(10px);
|
|
-webkit-backdrop-filter: blur(10px);
|
|
padding: 1rem 0;
|
|
position: sticky;
|
|
top: 0;
|
|
z-index: 100;
|
|
transition: all 0.3s ease;
|
|
border-bottom: 1px solid rgba(255, 255, 255, 0.05);
|
|
}
|
|
|
|
.site-header.scrolled {
|
|
padding: 0.6rem 0;
|
|
background: rgba(10, 12, 20, 0.95);
|
|
box-shadow: 0 5px 20px rgba(0, 0, 0, 0.2);
|
|
}
|
|
|
|
.header-container {
|
|
display: flex;
|
|
justify-content: space-between;
|
|
align-items: center;
|
|
}
|
|
|
|
.logo {
|
|
display: flex;
|
|
align-items: center;
|
|
gap: 0.75rem;
|
|
color: var(--text-primary);
|
|
text-decoration: none;
|
|
position: relative;
|
|
}
|
|
|
|
.logo-symbol {
|
|
width: 2.75rem;
|
|
height: 2.75rem;
|
|
border-radius: 10px;
|
|
background: linear-gradient(135deg, var(--accent-primary), var(--accent-secondary));
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: center;
|
|
position: relative;
|
|
overflow: hidden;
|
|
transition: transform 0.3s ease;
|
|
}
|
|
|
|
.logo:hover .logo-symbol {
|
|
transform: translateY(-3px);
|
|
}
|
|
|
|
.logo-text {
|
|
font-family: var(--font-mono);
|
|
font-weight: bold;
|
|
font-size: 1.25rem;
|
|
color: var(--bg-primary);
|
|
z-index: 2;
|
|
}
|
|
|
|
.logo-glow {
|
|
position: absolute;
|
|
width: 150%;
|
|
height: 150%;
|
|
background: radial-gradient(circle, var(--accent-primary) 0%, transparent 70%);
|
|
opacity: 0.5;
|
|
filter: blur(15px);
|
|
z-index: 1;
|
|
animation: pulse 4s infinite alternate ease-in-out;
|
|
}
|
|
|
|
.logo-text-container {
|
|
display: flex;
|
|
flex-direction: column;
|
|
}
|
|
|
|
.logo-name {
|
|
font-weight: 600;
|
|
font-size: 1.4rem;
|
|
background: linear-gradient(90deg, var(--accent-primary), var(--accent-secondary));
|
|
-webkit-background-clip: text;
|
|
background-clip: text;
|
|
color: transparent;
|
|
letter-spacing: 0.02em;
|
|
}
|
|
|
|
.logo-tagline {
|
|
font-size: 0.75rem;
|
|
color: var(--text-secondary);
|
|
font-family: var(--font-mono);
|
|
letter-spacing: 0.05em;
|
|
}
|
|
|
|
.main-nav {
|
|
display: flex;
|
|
gap: 1.5rem;
|
|
align-items: center;
|
|
}
|
|
|
|
.nav-link {
|
|
color: var(--text-secondary);
|
|
position: relative;
|
|
text-decoration: none;
|
|
font-weight: 500;
|
|
transition: color 0.3s ease;
|
|
padding: 0.5rem 0;
|
|
}
|
|
|
|
.nav-link:hover, .nav-link.active {
|
|
color: var(--text-primary);
|
|
}
|
|
|
|
.nav-highlight {
|
|
position: absolute;
|
|
bottom: -2px;
|
|
left: 0;
|
|
width: 0;
|
|
height: 2px;
|
|
background: linear-gradient(90deg, var(--accent-primary), var(--accent-secondary));
|
|
transition: width 0.3s ease;
|
|
border-radius: 2px;
|
|
}
|
|
|
|
.nav-link:hover .nav-highlight,
|
|
.nav-link.active .nav-highlight {
|
|
width: 100%;
|
|
}
|
|
|
|
.header-actions {
|
|
display: flex;
|
|
align-items: center;
|
|
gap: 1rem;
|
|
margin-left: 2rem;
|
|
}
|
|
|
|
.theme-toggle,
|
|
.search-button {
|
|
background: none;
|
|
border: none;
|
|
color: var(--text-secondary);
|
|
padding: 0.5rem;
|
|
cursor: pointer;
|
|
border-radius: 50%;
|
|
transition: all 0.3s ease;
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: center;
|
|
}
|
|
|
|
.theme-toggle:hover,
|
|
.search-button:hover {
|
|
color: var(--text-primary);
|
|
background: rgba(255, 255, 255, 0.1);
|
|
}
|
|
|
|
.icon-sun {
|
|
display: none;
|
|
}
|
|
|
|
:global(.light-mode) .icon-moon {
|
|
display: none;
|
|
}
|
|
|
|
:global(.light-mode) .icon-sun {
|
|
display: block;
|
|
}
|
|
|
|
.mobile-menu-btn {
|
|
display: none;
|
|
background: none;
|
|
border: none;
|
|
color: var(--text-primary);
|
|
cursor: pointer;
|
|
}
|
|
|
|
.icon-close {
|
|
display: none;
|
|
}
|
|
|
|
.mobile-menu-active .icon-menu {
|
|
display: none;
|
|
}
|
|
|
|
.mobile-menu-active .icon-close {
|
|
display: block;
|
|
}
|
|
|
|
/* Animated network lines effect */
|
|
.network-lines {
|
|
position: absolute;
|
|
bottom: -1px;
|
|
left: 0;
|
|
width: 100%;
|
|
height: 1px;
|
|
overflow: hidden;
|
|
opacity: 0.5;
|
|
}
|
|
|
|
.network-lines::before {
|
|
content: '';
|
|
position: absolute;
|
|
top: 0;
|
|
left: 0;
|
|
width: 100%;
|
|
height: 100%;
|
|
background: linear-gradient(90deg, transparent 0%, var(--accent-primary) 50%, transparent 100%);
|
|
animation: network-scan 8s infinite linear;
|
|
}
|
|
|
|
/* Nebula background */
|
|
.nebula-bg {
|
|
position: absolute;
|
|
top: 0;
|
|
left: 0;
|
|
width: 100%;
|
|
height: 100%;
|
|
background-image:
|
|
radial-gradient(circle at 20% 50%, rgba(6, 182, 212, 0.1) 0%, transparent 50%),
|
|
radial-gradient(circle at 80% 50%, rgba(139, 92, 246, 0.1) 0%, transparent 50%);
|
|
opacity: 0.3;
|
|
z-index: -1;
|
|
}
|
|
|
|
@keyframes pulse {
|
|
0% {
|
|
opacity: 0.4;
|
|
transform: scale(0.8);
|
|
}
|
|
100% {
|
|
opacity: 0.8;
|
|
transform: scale(1.2);
|
|
}
|
|
}
|
|
|
|
@keyframes network-scan {
|
|
0% {
|
|
transform: translateX(-100%);
|
|
}
|
|
100% {
|
|
transform: translateX(100%);
|
|
}
|
|
}
|
|
|
|
/* Responsive Adjustments */
|
|
@media (max-width: 1024px) {
|
|
.main-nav {
|
|
gap: 1rem;
|
|
}
|
|
|
|
.header-actions {
|
|
margin-left: 1rem;
|
|
}
|
|
}
|
|
|
|
@media (max-width: 768px) {
|
|
.mobile-menu-btn {
|
|
display: block;
|
|
}
|
|
|
|
.main-nav {
|
|
display: none;
|
|
position: absolute;
|
|
top: 100%;
|
|
left: 0;
|
|
width: 100%;
|
|
background: var(--bg-secondary);
|
|
padding: 1rem;
|
|
flex-direction: column;
|
|
align-items: flex-start;
|
|
gap: 1rem;
|
|
border-bottom: 1px solid var(--border-primary);
|
|
z-index: 10;
|
|
}
|
|
|
|
.main-nav.active {
|
|
display: flex;
|
|
}
|
|
|
|
.logo-tagline {
|
|
display: none;
|
|
}
|
|
|
|
.theme-toggle, .search-button {
|
|
padding: 0.4rem;
|
|
}
|
|
}
|
|
</style>
|
|
|
|
<script>
|
|
// Only define one DOMContentLoaded event handler
|
|
document.addEventListener('DOMContentLoaded', () => {
|
|
const menuBtn = document.getElementById('mobile-menu-btn');
|
|
const mainNav = document.querySelector('.main-nav');
|
|
const header = document.querySelector('.site-header');
|
|
|
|
// Mobile menu toggle
|
|
if (menuBtn && mainNav) {
|
|
menuBtn.addEventListener('click', () => {
|
|
mainNav.classList.toggle('active');
|
|
menuBtn.classList.toggle('mobile-menu-active');
|
|
});
|
|
}
|
|
|
|
// Header scroll effect
|
|
window.addEventListener('scroll', () => {
|
|
if (window.scrollY > 50) {
|
|
header?.classList.add('scrolled');
|
|
} else {
|
|
header?.classList.remove('scrolled');
|
|
}
|
|
});
|
|
|
|
// Theme toggle functionality - only define once
|
|
const themeToggle = document.getElementById('theme-toggle');
|
|
|
|
if (themeToggle) {
|
|
themeToggle.addEventListener('click', () => {
|
|
document.documentElement.classList.toggle('light-mode');
|
|
|
|
// Store preference in localStorage
|
|
const isLightMode = document.documentElement.classList.contains('light-mode');
|
|
localStorage.setItem('theme', isLightMode ? 'light' : 'dark');
|
|
});
|
|
|
|
// Apply saved theme preference
|
|
const savedTheme = localStorage.getItem('theme');
|
|
if (savedTheme === 'light') {
|
|
document.documentElement.classList.add('light-mode');
|
|
}
|
|
}
|
|
|
|
// Add interactive network nodes animation
|
|
const header_el = document.querySelector('.site-header');
|
|
|
|
if (header_el) {
|
|
// Create animated nodes
|
|
for (let i = 0; i < 5; i++) {
|
|
const node = document.createElement('div');
|
|
node.className = 'nav-node';
|
|
node.style.left = `${Math.random() * 100}%`;
|
|
node.style.animationDelay = `${Math.random() * 5}s`;
|
|
node.style.animationDuration = `${5 + Math.random() * 5}s`;
|
|
header_el.appendChild(node);
|
|
}
|
|
}
|
|
});
|
|
</script> |