- Introduced FounderSection component to highlight leadership and vision. - Created ServicesGrid component to display various robotics services offered. - Developed RoboticsScrollShowcase for showcasing robots with interactive elements. - Implemented RoboticsSplineShowcase featuring a 3D Spline scene for enhanced user experience. - Added reusable Card component for consistent styling across sections. - Integrated ContainerScroll for animated scrolling effects in the showcase. - Built SplineScene component for lazy loading Spline 3D scenes. - Added Spotlight component for interactive hover effects. - Created utility function for class name merging to streamline styling.
238 lines
8.3 KiB
TypeScript
238 lines
8.3 KiB
TypeScript
'use client';
|
|
|
|
import React, { useEffect, useState } from 'react';
|
|
import Link from 'next/link';
|
|
import Image from 'next/image';
|
|
import { usePathname } from 'next/navigation';
|
|
|
|
const NAV_LINKS = [
|
|
{ label: 'Home', href: '/' },
|
|
{ label: 'Robots', href: '/robots/' },
|
|
{ label: 'Brands', href: '/brands/' },
|
|
{ label: 'Industries', href: '/industries/' },
|
|
{ label: 'About', href: '/about/' },
|
|
{ label: 'Contact', href: '/contact/' },
|
|
{ label: 'Book Demo', href: '/book-demo/' },
|
|
];
|
|
|
|
export function Navbar() {
|
|
const [scrolled, setScrolled] = useState(false);
|
|
const [mobileOpen, setMobileOpen] = useState(false);
|
|
const pathname = usePathname() || '/';
|
|
|
|
useEffect(() => {
|
|
const onScroll = () => setScrolled(window.scrollY > 16);
|
|
onScroll();
|
|
window.addEventListener('scroll', onScroll, { passive: true });
|
|
return () => window.removeEventListener('scroll', onScroll);
|
|
}, []);
|
|
|
|
useEffect(() => {
|
|
document.body.style.overflow = mobileOpen ? 'hidden' : '';
|
|
return () => {
|
|
document.body.style.overflow = '';
|
|
};
|
|
}, [mobileOpen]);
|
|
|
|
const isActive = (href: string) => {
|
|
if (href === '/') return pathname === '/' || pathname === '';
|
|
return pathname.startsWith(href.replace(/\/$/, ''));
|
|
};
|
|
|
|
return (
|
|
<>
|
|
<nav
|
|
style={{
|
|
position: 'fixed',
|
|
top: 0,
|
|
left: 0,
|
|
right: 0,
|
|
zIndex: 100,
|
|
background: scrolled ? 'rgba(5, 5, 5, 0.78)' : 'linear-gradient(180deg, rgba(5, 5, 5,0.6), rgba(5, 5, 5,0))',
|
|
backdropFilter: scrolled ? 'blur(18px)' : 'blur(8px)',
|
|
WebkitBackdropFilter: scrolled ? 'blur(18px)' : 'blur(8px)',
|
|
borderBottom: scrolled ? '1px solid rgba(39, 63, 148,0.14)' : '1px solid transparent',
|
|
transition: 'all 0.35s cubic-bezier(0.16,1,0.3,1)',
|
|
}}
|
|
>
|
|
<div
|
|
style={{
|
|
maxWidth: '1320px',
|
|
margin: '0 auto',
|
|
padding: scrolled ? '0.85rem clamp(1rem, 4vw, 2rem)' : '1.1rem clamp(1rem, 4vw, 2rem)',
|
|
display: 'flex',
|
|
alignItems: 'center',
|
|
justifyContent: 'space-between',
|
|
gap: '1rem',
|
|
transition: 'padding 0.3s ease',
|
|
}}
|
|
>
|
|
<Link href="/" style={{ textDecoration: 'none', display: 'flex', alignItems: 'center', gap: '0.65rem', minWidth: 0 }} onClick={() => setMobileOpen(false)} aria-label="YS Lootah Robotics — Home">
|
|
<span
|
|
style={{
|
|
position: 'relative',
|
|
width: 40,
|
|
height: 40,
|
|
borderRadius: '50%',
|
|
overflow: 'hidden',
|
|
flex: 'none',
|
|
boxShadow: '0 0 18px rgba(39, 63, 148, 0.35)',
|
|
border: '1px solid rgba(39, 63, 148,0.4)',
|
|
}}
|
|
>
|
|
<Image
|
|
src="/images/brands/ys-lootah-robotics-logo.png"
|
|
alt="YS Lootah Robotics logo"
|
|
fill
|
|
sizes="40px"
|
|
style={{ objectFit: 'cover' }}
|
|
priority
|
|
/>
|
|
</span>
|
|
<span style={{ display: 'flex', flexDirection: 'column', lineHeight: 1 }}>
|
|
<span style={{ fontSize: '0.95rem', fontWeight: 700, letterSpacing: '0.16em', color: '#ffffff', textTransform: 'uppercase' }}>
|
|
YS Lootah
|
|
</span>
|
|
<span style={{ fontSize: '0.7rem', fontWeight: 500, letterSpacing: '0.32em', color: '#DEE0F0', textTransform: 'uppercase', marginTop: 3 }}>
|
|
Robotics
|
|
</span>
|
|
</span>
|
|
</Link>
|
|
|
|
<div className="nav-desktop" style={{ alignItems: 'center', gap: '1.4rem' }}>
|
|
{NAV_LINKS.map((l) => (
|
|
<Link
|
|
key={l.href}
|
|
href={l.href}
|
|
style={{
|
|
position: 'relative',
|
|
color: isActive(l.href) ? '#DEE0F0' : '#DEE0F0',
|
|
fontSize: '0.78rem',
|
|
fontWeight: 600,
|
|
textDecoration: 'none',
|
|
letterSpacing: '0.16em',
|
|
textTransform: 'uppercase',
|
|
transition: 'color 0.2s',
|
|
paddingBottom: 4,
|
|
borderBottom: isActive(l.href) ? '1px solid #DEE0F0' : '1px solid transparent',
|
|
}}
|
|
>
|
|
{l.label}
|
|
</Link>
|
|
))}
|
|
</div>
|
|
|
|
<div style={{ display: 'flex', alignItems: 'center', gap: '0.625rem' }}>
|
|
<Link
|
|
href="/configure/"
|
|
className="nav-desktop-cta btn btn-primary"
|
|
style={{ padding: '0.7rem 1.2rem', fontSize: '0.78rem' }}
|
|
>
|
|
Configure
|
|
</Link>
|
|
<button
|
|
type="button"
|
|
className="nav-mobile-toggle"
|
|
aria-label={mobileOpen ? 'Close menu' : 'Open menu'}
|
|
aria-expanded={mobileOpen}
|
|
onClick={() => setMobileOpen((s) => !s)}
|
|
style={{
|
|
width: 42,
|
|
height: 42,
|
|
borderRadius: 12,
|
|
border: '1px solid rgba(39, 63, 148,0.2)',
|
|
background: 'rgba(15, 12, 8,0.6)',
|
|
color: '#FBFBFD',
|
|
cursor: 'pointer',
|
|
display: 'none',
|
|
alignItems: 'center',
|
|
justifyContent: 'center',
|
|
padding: 0,
|
|
}}
|
|
>
|
|
<svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round">
|
|
{mobileOpen ? <path d="M6 6l12 12M6 18L18 6" /> : <path d="M4 7h16M4 12h16M4 17h16" />}
|
|
</svg>
|
|
</button>
|
|
</div>
|
|
</div>
|
|
</nav>
|
|
|
|
<div
|
|
role="dialog"
|
|
aria-modal="true"
|
|
aria-label="Mobile navigation"
|
|
style={{
|
|
position: 'fixed',
|
|
inset: 0,
|
|
zIndex: 99,
|
|
background: 'rgba(5, 5, 5,0.96)',
|
|
backdropFilter: 'blur(20px)',
|
|
WebkitBackdropFilter: 'blur(20px)',
|
|
transform: mobileOpen ? 'translateY(0)' : 'translateY(-12px)',
|
|
opacity: mobileOpen ? 1 : 0,
|
|
pointerEvents: mobileOpen ? 'auto' : 'none',
|
|
transition: 'opacity 0.25s ease, transform 0.25s ease',
|
|
padding: 'clamp(5rem, 14vw, 7rem) clamp(1.5rem, 5vw, 2rem) clamp(2rem, 6vw, 3rem)',
|
|
display: 'flex',
|
|
flexDirection: 'column',
|
|
gap: '1rem',
|
|
overflowY: 'auto',
|
|
}}
|
|
>
|
|
{NAV_LINKS.map((l) => (
|
|
<Link
|
|
key={l.href}
|
|
href={l.href}
|
|
onClick={() => setMobileOpen(false)}
|
|
style={{
|
|
fontSize: '1.5rem',
|
|
fontWeight: 500,
|
|
color: isActive(l.href) ? '#DEE0F0' : '#FBFBFD',
|
|
textDecoration: 'none',
|
|
letterSpacing: '-0.01em',
|
|
padding: '0.75rem 0',
|
|
borderBottom: '1px solid rgba(39, 63, 148,0.12)',
|
|
display: 'flex',
|
|
justifyContent: 'space-between',
|
|
alignItems: 'center',
|
|
}}
|
|
>
|
|
<span>{l.label}</span>
|
|
<svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round" aria-hidden="true">
|
|
<line x1="5" y1="12" x2="19" y2="12" />
|
|
<polyline points="12 5 19 12 12 19" />
|
|
</svg>
|
|
</Link>
|
|
))}
|
|
<Link
|
|
href="/configure/"
|
|
onClick={() => setMobileOpen(false)}
|
|
className="btn btn-primary"
|
|
style={{ marginTop: '1rem', justifyContent: 'center' }}
|
|
>
|
|
Configure your robot
|
|
</Link>
|
|
<Link
|
|
href="/book-demo/"
|
|
onClick={() => setMobileOpen(false)}
|
|
className="btn btn-ghost"
|
|
style={{ justifyContent: 'center' }}
|
|
>
|
|
Book a demo
|
|
</Link>
|
|
</div>
|
|
|
|
<style jsx>{`
|
|
.nav-desktop, .nav-desktop-cta { display: none !important; }
|
|
.nav-mobile-toggle { display: inline-flex !important; }
|
|
@media (min-width: 1080px) {
|
|
.nav-desktop { display: flex !important; }
|
|
.nav-desktop-cta { display: inline-flex !important; }
|
|
.nav-mobile-toggle { display: none !important; }
|
|
}
|
|
`}</style>
|
|
</>
|
|
);
|
|
}
|