feat: modernize mobile bottom nav with 2030 design and fix z-index
All checks were successful
Build & Deploy / 🔍 Prepare (push) Successful in 30s
Build & Deploy / 🧪 QA (push) Successful in 1m24s
Build & Deploy / 🏗️ Build (push) Successful in 3m5s
Build & Deploy / 🚀 Deploy (push) Successful in 35s
Build & Deploy / 🧪 Post-Deploy Verification (push) Successful in 58s
Build & Deploy / 🔔 Notify (push) Successful in 3s

This commit is contained in:
2026-06-19 22:39:13 +02:00
parent 11e7937e79
commit efb9bdbb3f

View File

@@ -24,8 +24,6 @@ export function MobileBottomNav({ navLinks, currentLocale }: MobileBottomNavProp
const homeUrl = `/${currentLocale}`; const homeUrl = `/${currentLocale}`;
// Find the specific links from the provided navLinks (which might be translated) // Find the specific links from the provided navLinks (which might be translated)
// We do a simple match based on index or keywords if needed, but since we know the order:
// 0: Kompetenzen, 1: Über uns, 2: Karriere, 3: Messen
const competenciesUrl = navLinks[0]?.url || `/${currentLocale}/kompetenzen`; const competenciesUrl = navLinks[0]?.url || `/${currentLocale}/kompetenzen`;
const aboutUrl = navLinks[1]?.url || `/${currentLocale}/ueber-uns`; const aboutUrl = navLinks[1]?.url || `/${currentLocale}/ueber-uns`;
const contactUrl = `/${currentLocale}/${currentLocale === 'de' ? 'kontakt' : 'contact'}`; const contactUrl = `/${currentLocale}/${currentLocale === 'de' ? 'kontakt' : 'contact'}`;
@@ -34,30 +32,30 @@ export function MobileBottomNav({ navLinks, currentLocale }: MobileBottomNavProp
{ {
label: currentLocale === 'de' ? 'Start' : 'Home', label: currentLocale === 'de' ? 'Start' : 'Home',
url: homeUrl, url: homeUrl,
icon: <Home className="w-6 h-6 mb-1" strokeWidth={2} />, icon: <Home className="w-5 h-5 md:w-6 md:h-6" strokeWidth={2.5} />,
exactMatch: true exactMatch: true
}, },
{ {
label: navLinks[0]?.label || (currentLocale === 'de' ? 'Kompetenzen' : 'Competencies'), label: navLinks[0]?.label || (currentLocale === 'de' ? 'Kompetenzen' : 'Competencies'),
url: competenciesUrl, url: competenciesUrl,
icon: <Briefcase className="w-6 h-6 mb-1" strokeWidth={2} /> icon: <Briefcase className="w-5 h-5 md:w-6 md:h-6" strokeWidth={2.5} />
}, },
{ {
label: navLinks[1]?.label || (currentLocale === 'de' ? 'Über uns' : 'About Us'), label: navLinks[1]?.label || (currentLocale === 'de' ? 'Über uns' : 'About Us'),
url: aboutUrl, url: aboutUrl,
icon: <Users className="w-6 h-6 mb-1" strokeWidth={2} /> icon: <Users className="w-5 h-5 md:w-6 md:h-6" strokeWidth={2.5} />
}, },
{ {
label: currentLocale === 'de' ? 'Kontakt' : 'Contact', label: currentLocale === 'de' ? 'Kontakt' : 'Contact',
url: contactUrl, url: contactUrl,
icon: <Mail className="w-6 h-6 mb-1" strokeWidth={2} /> icon: <Mail className="w-5 h-5 md:w-6 md:h-6" strokeWidth={2.5} />
} }
]; ];
}, [currentLocale, navLinks]); }, [currentLocale, navLinks]);
return ( return (
<div className="md:hidden fixed bottom-0 left-0 right-0 z-50 px-2 pb-2 pt-2 bg-gradient-to-t from-white via-white to-white/90 backdrop-blur-xl border-t border-neutral-200/50 shadow-[0_-8px_32px_rgba(0,0,0,0.05)] safe-area-bottom"> <div className="md:hidden fixed bottom-0 left-0 right-0 z-[9999] px-4 pb-[calc(env(safe-area-inset-bottom)+1rem)] pt-4 pointer-events-none flex justify-center">
<nav className="flex justify-around items-center max-w-md mx-auto"> <nav className="flex justify-between items-center bg-white/70 backdrop-blur-3xl border border-white/60 shadow-[0_8px_32px_rgba(0,0,0,0.12)] rounded-3xl p-1.5 pointer-events-auto w-full max-w-[400px]">
{items.map((item) => { {items.map((item) => {
// Determine if active // Determine if active
const mappedUrl = item.url.startsWith('/') && !item.url.match(/^\/(en|de)/) const mappedUrl = item.url.startsWith('/') && !item.url.match(/^\/(en|de)/)
@@ -72,26 +70,34 @@ export function MobileBottomNav({ navLinks, currentLocale }: MobileBottomNavProp
<TransitionLink <TransitionLink
key={item.label} key={item.label}
href={mappedUrl} href={mappedUrl}
className={`relative flex flex-col items-center justify-center w-full min-h-[44px] py-1 transition-colors duration-300 select-none ${ className={`relative flex flex-col items-center justify-center flex-1 h-[60px] transition-colors duration-300 select-none ${
isActive ? 'text-primary' : 'text-neutral-500 hover:text-neutral-900' isActive ? 'text-white' : 'text-neutral-500 hover:text-neutral-900'
}`} }`}
style={{ WebkitTapHighlightColor: 'transparent', touchAction: 'manipulation' }}
> >
<div className="relative flex flex-col items-center justify-center"> {isActive && (
{/* Active Icon Background */} <motion.div
{isActive && ( layoutId="mobile-nav-active-bg"
<motion.div className="absolute inset-0 bg-primary rounded-2xl shadow-md"
layoutId="mobile-nav-active-bg" transition={{ type: 'spring', stiffness: 400, damping: 25 }}
className="absolute inset-0 bg-primary/10 rounded-full scale-150" />
transition={{ type: 'spring', stiffness: 500, damping: 30 }} )}
/>
)}
{/* Icon wrapper to ensure it sits above the background */} <div className="relative flex flex-col items-center justify-center z-10 w-full h-full">
<div className="relative z-10 transition-transform duration-300 active:scale-90"> <motion.div
whileTap={{ scale: 0.8 }}
animate={{ y: isActive ? -2 : 0 }}
transition={{ type: 'spring', stiffness: 400, damping: 17 }}
>
{item.icon} {item.icon}
</div> </motion.div>
<motion.span
animate={{ opacity: isActive ? 1 : 0.7, y: isActive ? 0 : 2 }}
className="text-[10px] font-bold mt-1 tracking-wide"
>
{item.label}
</motion.span>
</div> </div>
<span className="text-[10px] font-semibold mt-0.5 tracking-wide z-10">{item.label}</span>
</TransitionLink> </TransitionLink>
); );
})} })}