TypeScript 93.3%
JavaScript 4.4%
CSS 2.3%
1/**2 * Author: Simon-Pierre Boucher3 * Contact: contact@spboucher.ai4 * Project: Hilmacorp.ai — Web Platform5 * File: components/Header.tsx6 * Description: Sticky site header with desktop navigation, mobile menu, and locale switcher (light theme)7 */89"use client";1011import { useState } from "react";12import Link from "next/link";13import { usePathname } from "next/navigation";14import Logo from "@/components/Logo";15import LocaleSwitcher from "@/components/LocaleSwitcher";1617interface NavStrings {18 home: string;19 about: string;20 founders: string;21 services: string;22 security: string;23 industries: string;24 apps: string;25 contact: string;26 cta: string;27 openMenu: string;28 closeMenu: string;29 switchLocale: string;30}3132export default function Header({33 locale,34 nav,35}: {36 locale: "fr" | "en";37 nav: NavStrings;38}) {39 const [open, setOpen] = useState(false);40 const pathname = usePathname() ?? "";4142 const links = [43 { href: `/${locale}/about`, label: nav.about },44 { href: `/${locale}/founders`, label: nav.founders },45 { href: `/${locale}/services`, label: nav.services },46 { href: `/${locale}/security`, label: nav.security },47 { href: `/${locale}/industries`, label: nav.industries },48 { href: `/${locale}/apps`, label: nav.apps },49 { href: `/${locale}/contact`, label: nav.contact },50 ];5152 return (53 <header className="sticky top-0 z-50 border-b border-line bg-paper/90 backdrop-blur-md">54 <div className="mx-auto flex h-[68px] w-full max-w-6xl items-center justify-between px-5 sm:px-8">55 <Link href={`/${locale}`} aria-label={nav.home} onClick={() => setOpen(false)}>56 <Logo />57 </Link>5859 <nav className="hidden items-center gap-1 lg:flex" aria-label="Main">60 {links.map((link) => {61 const active = pathname.startsWith(link.href);62 return (63 <Link64 key={link.href}65 href={link.href}66 className={`rounded-full px-3.5 py-1.5 text-sm transition-colors ${67 active68 ? "bg-accent-soft text-accent-strong"69 : "text-ink-soft hover:text-ink"70 }`}71 aria-current={active ? "page" : undefined}72 >73 {link.label}74 </Link>75 );76 })}77 </nav>7879 <div className="hidden items-center gap-3 lg:flex">80 <LocaleSwitcher locale={locale} label={nav.switchLocale} />81 <Link82 href={`/${locale}/contact`}83 className="rounded-full bg-ink px-4.5 py-2 text-sm font-medium text-paper transition-colors hover:bg-accent-strong"84 >85 {nav.cta}86 </Link>87 </div>8889 <div className="flex items-center gap-3 lg:hidden">90 <LocaleSwitcher locale={locale} label={nav.switchLocale} />91 <button92 type="button"93 onClick={() => setOpen(!open)}94 aria-expanded={open}95 aria-label={open ? nav.closeMenu : nav.openMenu}96 className="flex h-9 w-9 items-center justify-center rounded-lg border border-line-strong text-ink"97 >98 <svg width="18" height="18" viewBox="0 0 18 18" fill="none" aria-hidden="true">99 {open ? (100 <path d="M4 4l10 10M14 4L4 14" stroke="currentColor" strokeWidth="1.8" strokeLinecap="round" />101 ) : (102 <path d="M2 5h14M2 9h14M2 13h14" stroke="currentColor" strokeWidth="1.8" strokeLinecap="round" />103 )}104 </svg>105 </button>106 </div>107 </div>108109 {open ? (110 <nav111 className="border-t border-line bg-paper px-5 pb-6 pt-3 lg:hidden"112 aria-label="Mobile"113 >114 <ul className="flex flex-col">115 {links.map((link) => (116 <li key={link.href}>117 <Link118 href={link.href}119 onClick={() => setOpen(false)}120 className="block rounded-lg px-2 py-3 text-base text-ink-soft hover:bg-paper-2 hover:text-ink"121 >122 {link.label}123 </Link>124 </li>125 ))}126 </ul>127 <Link128 href={`/${locale}/contact`}129 onClick={() => setOpen(false)}130 className="mt-3 block rounded-full bg-ink px-4 py-2.5 text-center text-sm font-medium text-paper"131 >132 {nav.cta}133 </Link>134 </nav>135 ) : null}136 </header>137 );138}139