feat: modernize digital presence, team layout, and hero component parity
Some checks failed
Build & Deploy / 🔍 Prepare (push) Successful in 17s
Build & Deploy / 🧪 QA (push) Failing after 59s
Build & Deploy / 🏗️ Build (push) Has been skipped
Build & Deploy / 🚀 Deploy (push) Has been skipped
Build & Deploy / 🧪 Post-Deploy Verification (push) Has been skipped
Build & Deploy / 🔔 Notify (push) Successful in 1s
Some checks failed
Build & Deploy / 🔍 Prepare (push) Successful in 17s
Build & Deploy / 🧪 QA (push) Failing after 59s
Build & Deploy / 🏗️ Build (push) Has been skipped
Build & Deploy / 🚀 Deploy (push) Has been skipped
Build & Deploy / 🧪 Post-Deploy Verification (push) Has been skipped
Build & Deploy / 🔔 Notify (push) Successful in 1s
This commit is contained in:
@@ -4,12 +4,13 @@ import * as React from 'react';
|
||||
import { TransitionLink } from '@/components/ui/TransitionLink';
|
||||
import Image from 'next/image';
|
||||
import { usePathname } from 'next/navigation';
|
||||
import { motion, useScroll, useTransform } from 'framer-motion';
|
||||
import { motion, useScroll, useTransform, AnimatePresence } from 'framer-motion';
|
||||
import { LanguageSwitcher } from './LanguageSwitcher';
|
||||
|
||||
export interface NavLink {
|
||||
label: string;
|
||||
url: string;
|
||||
children?: NavLink[];
|
||||
}
|
||||
|
||||
interface HeaderProps {
|
||||
@@ -71,7 +72,7 @@ export function Header({ navLinks }: HeaderProps) {
|
||||
}`}
|
||||
>
|
||||
<div
|
||||
className={`relative overflow-hidden w-full max-w-7xl mx-auto transition-all duration-700 ease-[cubic-bezier(0.16,1,0.3,1)] flex items-center justify-between ${
|
||||
className={`relative w-full max-w-7xl mx-auto transition-all duration-700 ease-[cubic-bezier(0.16,1,0.3,1)] flex items-center justify-between ${
|
||||
isSolidMode
|
||||
? 'rounded-full bg-white/50 backdrop-blur-xl shadow-[0_8px_32px_rgba(0,0,0,0.08)] border border-white/40 py-2.5 px-6 md:px-8'
|
||||
: 'rounded-none bg-transparent py-6 px-4 md:px-8 border border-transparent'
|
||||
@@ -114,40 +115,15 @@ export function Header({ navLinks }: HeaderProps) {
|
||||
|
||||
<div className="flex items-center gap-2 md:gap-4 ml-auto md:ml-0">
|
||||
<nav className="relative z-10 hidden md:flex items-center gap-8 lg:gap-10">
|
||||
{navLinks && navLinks.length > 0 && navLinks.map((link) => {
|
||||
const mappedUrl = link.url.startsWith('/') && !link.url.match(/^\/(en|de)/)
|
||||
? `/${currentLocale}${link.url}`
|
||||
: link.url;
|
||||
|
||||
const isActive = pathname === mappedUrl || (mappedUrl !== `/${currentLocale}` && pathname?.startsWith(`${mappedUrl}/`));
|
||||
|
||||
return (
|
||||
<TransitionLink
|
||||
key={link.url}
|
||||
href={mappedUrl}
|
||||
className={`relative font-bold text-xs lg:text-sm uppercase tracking-widest transition-colors duration-300 group py-2 select-none ${
|
||||
isSolidMode
|
||||
? (isActive ? 'text-primary' : 'text-text-primary hover:text-primary')
|
||||
: (isActive ? 'text-white' : 'text-white/80 hover:text-white')
|
||||
}`}
|
||||
>
|
||||
{link.label}
|
||||
|
||||
{/* Framer Motion Active Indicator */}
|
||||
{isActive && (
|
||||
<motion.div
|
||||
layoutId="header-active-indicator"
|
||||
className={`absolute -bottom-1 left-0 right-0 h-0.5 rounded-full ${isSolidMode ? 'bg-primary' : 'bg-white'}`}
|
||||
transition={{ type: 'spring', stiffness: 400, damping: 30 }}
|
||||
/>
|
||||
)}
|
||||
|
||||
{/* Subtle hover underline */}
|
||||
<span className={`absolute -bottom-1 left-1/2 w-0 h-0.5 -translate-x-1/2 rounded-full transition-all duration-300 ease-out group-hover:w-full ${isSolidMode ? 'bg-primary/30' : 'bg-white/30'} ${isActive ? 'opacity-0' : 'opacity-100'}`} />
|
||||
</TransitionLink>
|
||||
);
|
||||
})}
|
||||
|
||||
{navLinks && navLinks.length > 0 && navLinks.map((link) => (
|
||||
<NavItem
|
||||
key={link.url}
|
||||
link={link}
|
||||
currentLocale={currentLocale}
|
||||
pathname={pathname}
|
||||
isSolidMode={isSolidMode}
|
||||
/>
|
||||
))}
|
||||
</nav>
|
||||
|
||||
{/* Right side elements (Language Switcher always visible, Contact button desktop only) */}
|
||||
@@ -180,3 +156,83 @@ export function Header({ navLinks }: HeaderProps) {
|
||||
);
|
||||
}
|
||||
|
||||
function NavItem({ link, currentLocale, pathname, isSolidMode }: { link: NavLink, currentLocale: string, pathname: string, isSolidMode: boolean }) {
|
||||
const [isHovered, setIsHovered] = React.useState(false);
|
||||
|
||||
const mappedUrl = link.url.startsWith('/') && !link.url.match(/^\/(en|de)/)
|
||||
? `/${currentLocale}${link.url}`
|
||||
: link.url;
|
||||
|
||||
const isActive = pathname === mappedUrl || (mappedUrl !== `/${currentLocale}` && pathname?.startsWith(`${mappedUrl}/`));
|
||||
|
||||
return (
|
||||
<div
|
||||
className="relative group py-2"
|
||||
onMouseEnter={() => setIsHovered(true)}
|
||||
onMouseLeave={() => setIsHovered(false)}
|
||||
>
|
||||
<TransitionLink
|
||||
href={mappedUrl}
|
||||
className={`relative font-bold text-xs lg:text-sm uppercase tracking-widest transition-colors duration-300 group flex items-center gap-1.5 select-none ${
|
||||
isSolidMode
|
||||
? (isActive ? 'text-primary' : 'text-text-primary hover:text-primary')
|
||||
: (isActive ? 'text-white' : 'text-white/80 hover:text-white')
|
||||
}`}
|
||||
>
|
||||
{link.label}
|
||||
{link.children && (
|
||||
<svg className={`w-3.5 h-3.5 transition-transform duration-300 ${isHovered ? 'rotate-180' : ''}`} fill="none" viewBox="0 0 24 24" stroke="currentColor">
|
||||
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2.5} d="M19 9l-7 7-7-7" />
|
||||
</svg>
|
||||
)}
|
||||
|
||||
{/* Framer Motion Active Indicator */}
|
||||
{isActive && !link.children && (
|
||||
<motion.div
|
||||
layoutId="header-active-indicator"
|
||||
className={`absolute -bottom-1 left-0 right-0 h-0.5 rounded-full ${isSolidMode ? 'bg-primary' : 'bg-white'}`}
|
||||
transition={{ type: 'spring', stiffness: 400, damping: 30 }}
|
||||
/>
|
||||
)}
|
||||
|
||||
{/* Subtle hover underline */}
|
||||
<span className={`absolute -bottom-1 left-1/2 w-0 h-0.5 -translate-x-1/2 rounded-full transition-all duration-300 ease-out group-hover:w-full ${isSolidMode ? 'bg-primary/30' : 'bg-white/30'} ${isActive ? 'opacity-0' : 'opacity-100'}`} />
|
||||
</TransitionLink>
|
||||
|
||||
<AnimatePresence>
|
||||
{isHovered && link.children && (
|
||||
<motion.div
|
||||
initial={{ opacity: 0, y: 10, scale: 0.95 }}
|
||||
animate={{ opacity: 1, y: 0, scale: 1 }}
|
||||
exit={{ opacity: 0, y: 10, scale: 0.95 }}
|
||||
transition={{ duration: 0.2, ease: 'easeOut' }}
|
||||
className="absolute top-full left-1/2 -translate-x-1/2 pt-4 w-56"
|
||||
>
|
||||
<div className={`p-2 rounded-3xl shadow-[0_20px_40px_-15px_rgba(0,0,0,0.1)] border ${isSolidMode ? 'bg-white/95 border-neutral-200/60' : 'bg-[#050B14]/90 border-white/10'} backdrop-blur-xl flex flex-col gap-1`}>
|
||||
{link.children.map((child) => {
|
||||
const childUrl = child.url.startsWith('/') && !child.url.match(/^\/(en|de)/)
|
||||
? `/${currentLocale}${child.url}`
|
||||
: child.url;
|
||||
const isChildActive = pathname === childUrl;
|
||||
return (
|
||||
<TransitionLink
|
||||
key={child.url}
|
||||
href={childUrl}
|
||||
className={`block px-4 py-3 rounded-2xl text-sm font-medium transition-all ${
|
||||
isSolidMode
|
||||
? isChildActive ? 'bg-primary/10 text-primary' : 'text-neutral-600 hover:bg-neutral-100 hover:text-primary'
|
||||
: isChildActive ? 'bg-white/10 text-white' : 'text-neutral-300 hover:bg-white/5 hover:text-white'
|
||||
}`}
|
||||
>
|
||||
{child.label}
|
||||
</TransitionLink>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
</motion.div>
|
||||
)}
|
||||
</AnimatePresence>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user