feat: overhaul competence pages, unify brand colors, and polish site integrity
Some checks failed
Build & Deploy / 🔍 Prepare (push) Successful in 23s
Build & Deploy / 🧪 QA (push) Failing after 1m8s
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 23s
Build & Deploy / 🧪 QA (push) Failing after 1m8s
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
- Rewrote all 5 competence pages (Kabeltiefbau, Bohrtechnik, Glasfaser, Planung, Vermessung) with factual data from references - Unified brand colors: replaced toxic neon green with brand-aligned teal (#10a379) - Fixed ReferencesSlider injection in dynamic MDX pages - Enhanced Footer with icon parity, improved branding placement, and unified hover effects - Refined Header with 3D micro-animations and improved dropdown visibility - Fixed localized tracking API (rewrites for /stats/api/send) - Resolved ReferenceErrors in InteractiveGermanyMap and Header state management - Cleaned reference titles and truncated metadata in lists
This commit is contained in:
@@ -20,6 +20,7 @@ interface HeaderProps {
|
||||
export function Header({ navLinks }: HeaderProps) {
|
||||
const [isScrolled, setIsScrolled] = React.useState(false);
|
||||
const [forceSolid, setForceSolid] = React.useState(false);
|
||||
const [headerVariant, setHeaderVariant] = React.useState<'capsule' | 'standard'>('capsule');
|
||||
const pathname = usePathname() || '/';
|
||||
|
||||
// Scroll bound shine effect (continuous looping as user scrolls)
|
||||
@@ -41,17 +42,18 @@ export function Header({ navLinks }: HeaderProps) {
|
||||
window.addEventListener('scroll', handleScroll);
|
||||
|
||||
// Check for forced solid header (used by error pages, legal pages without hero, etc.)
|
||||
const checkSolid = () => {
|
||||
const checkHeaderState = () => {
|
||||
setForceSolid(document.body.getAttribute('data-force-solid-header') === 'true');
|
||||
setHeaderVariant((document.body.getAttribute('data-header-variant') as 'capsule' | 'standard') || 'capsule');
|
||||
};
|
||||
checkSolid();
|
||||
checkHeaderState();
|
||||
|
||||
// Set up observer for client-side navigation changes
|
||||
const observer = new MutationObserver(checkSolid);
|
||||
observer.observe(document.body, { attributes: true, attributeFilter: ['data-force-solid-header'] });
|
||||
const observer = new MutationObserver(checkHeaderState);
|
||||
observer.observe(document.body, { attributes: true, attributeFilter: ['data-force-solid-header', 'data-header-variant'] });
|
||||
|
||||
// Re-check when pathname changes
|
||||
checkSolid();
|
||||
checkHeaderState();
|
||||
|
||||
return () => {
|
||||
window.removeEventListener('scroll', handleScroll);
|
||||
@@ -59,23 +61,26 @@ export function Header({ navLinks }: HeaderProps) {
|
||||
};
|
||||
}, [pathname]);
|
||||
|
||||
// Check for force solid initially
|
||||
|
||||
const isSolidMode = isScrolled || forceSolid;
|
||||
|
||||
const isSolidMode = isScrolled || forceSolid || headerVariant === 'standard';
|
||||
const isStandard = headerVariant === 'standard';
|
||||
|
||||
return (
|
||||
<>
|
||||
<header
|
||||
className={`fixed top-0 left-0 right-0 z-50 flex justify-center transition-all duration-700 ease-[cubic-bezier(0.16,1,0.3,1)] ${
|
||||
isSolidMode ? 'pt-4 px-4' : 'pt-0 px-0'
|
||||
isStandard
|
||||
? 'bg-[#050B14]/95 backdrop-blur-xl border-b border-white/10'
|
||||
: (isSolidMode ? 'pt-4 px-4' : 'pt-0 px-0')
|
||||
}`}
|
||||
>
|
||||
<div
|
||||
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'
|
||||
isStandard
|
||||
? 'py-4 px-6 md:px-8'
|
||||
: 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'
|
||||
}`}
|
||||
>
|
||||
{/* Scroll bound shine effect */}
|
||||
@@ -202,13 +207,17 @@ function NavItem({ link, currentLocale, pathname, isSolidMode }: { link: NavLink
|
||||
<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"
|
||||
initial={{ opacity: 0, y: 10, scale: 0.95, rotateX: -10 }}
|
||||
animate={{ opacity: 1, y: 0, scale: 1, rotateX: 0 }}
|
||||
exit={{ opacity: 0, y: 10, scale: 0.95, rotateX: -10 }}
|
||||
transition={{ duration: 0.3, ease: [0.16, 1, 0.3, 1] }}
|
||||
style={{ perspective: 1000, transformOrigin: 'top center' }}
|
||||
className="absolute top-full left-1/2 -translate-x-1/2 pt-6 w-64 z-50"
|
||||
>
|
||||
<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`}>
|
||||
<div className={`p-2.5 rounded-3xl shadow-[0_30px_60px_-15px_rgba(0,0,0,0.3)] border ${isSolidMode ? 'bg-white/95 border-neutral-200/60' : 'bg-[#050B14]/95 border-white/[0.08] shadow-[0_0_40px_rgba(0,0,0,0.4)]'} backdrop-blur-2xl flex flex-col gap-1 relative overflow-hidden`}>
|
||||
{/* Top glowing accent line */}
|
||||
<div className="absolute top-0 left-0 w-full h-[2px] bg-gradient-to-r from-transparent via-primary to-transparent opacity-70" />
|
||||
|
||||
{link.children.map((child) => {
|
||||
const childUrl = child.url.startsWith('/') && !child.url.match(/^\/(en|de)/)
|
||||
? `/${currentLocale}${child.url}`
|
||||
@@ -218,13 +227,21 @@ function NavItem({ link, currentLocale, pathname, isSolidMode }: { link: NavLink
|
||||
<TransitionLink
|
||||
key={child.url}
|
||||
href={childUrl}
|
||||
className={`block px-4 py-3 rounded-2xl text-sm font-medium transition-all ${
|
||||
className={`group/dropitem relative block px-4 py-3.5 rounded-2xl text-sm font-medium transition-all duration-300 overflow-hidden ${
|
||||
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'
|
||||
? isChildActive ? 'bg-primary/10 text-primary' : 'text-neutral-600 hover:text-primary hover:bg-neutral-50/80'
|
||||
: isChildActive ? 'bg-white/10 text-white' : 'text-neutral-300 hover:text-white hover:bg-white/[0.06]'
|
||||
}`}
|
||||
>
|
||||
{child.label}
|
||||
<div className="relative z-10 flex items-center justify-between">
|
||||
<span className="relative">
|
||||
{child.label}
|
||||
<span className={`absolute -bottom-0.5 left-0 w-0 h-0.5 rounded-full transition-all duration-300 ease-[cubic-bezier(0.16,1,0.3,1)] group-hover/dropitem:w-full ${isSolidMode ? 'bg-primary/40' : 'bg-primary/60'} ${isChildActive ? 'opacity-0' : 'opacity-100'}`} />
|
||||
</span>
|
||||
<svg className={`w-4 h-4 transition-all duration-300 ease-[cubic-bezier(0.16,1,0.3,1)] -translate-x-3 opacity-0 group-hover/dropitem:translate-x-0 group-hover/dropitem:opacity-100 ${isSolidMode ? 'text-primary' : 'text-white'}`} fill="none" viewBox="0 0 24 24" stroke="currentColor">
|
||||
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2.5} d="M9 5l7 7-7 7" />
|
||||
</svg>
|
||||
</div>
|
||||
</TransitionLink>
|
||||
);
|
||||
})}
|
||||
|
||||
Reference in New Issue
Block a user