This commit is contained in:
2026-08-08 09:12:25 +00:00
parent 4375781521
commit db30ed2198
2 changed files with 119 additions and 44 deletions

View File

@@ -7,7 +7,7 @@ import {
HeadContent,
Scripts,
} from "@tanstack/react-router";
import { useState, type ReactNode } from "react";
import { useEffect, useRef, useState, type ReactNode } from "react";
import { Menu, X, Phone, ChevronDown } from "lucide-react";
import { ScrollToTop } from "@/components/scroll-to-top";
import { QuickContact } from "@/components/quick-contact";
@@ -173,11 +173,26 @@ function Header() {
const [open, setOpen] = useState(false);
return (
<>
<header className="sticky top-0 z-50 bg-background/95 backdrop-blur border-b">
<div className="container-page flex items-center justify-center gap-6 h-20">
<div className="container-page flex items-center justify-between gap-6 h-20 lg:justify-center">
{/* Mobile: hamburger left, Promist beside it, Profen pushed right.
Desktop: lg:justify-center restores the original centered layout. */}
<div className="flex items-center gap-3">
<button
onClick={() => setOpen((v) => !v)}
className="lg:hidden inline-flex items-center justify-center h-10 w-10 rounded-md border shrink-0"
aria-label={open ? "Close menu" : "Open menu"}
aria-expanded={open}
aria-controls="mobile-menu"
>
{open ? <X className="h-5 w-5" /> : <Menu className="h-5 w-5" />}
</button>
<Link to="/" className="flex items-center shrink-0">
<img src="/promist-logo.png" alt="Promist" className="h-10 w-auto" />
</Link>
</div>
<span aria-hidden="true" className="hidden lg:block h-6 w-px bg-border" />
<nav className="hidden lg:flex items-center gap-6 text-sm font-medium">
{NAV_ITEMS.map((item) =>
"children" in item && item.children ? (
@@ -195,27 +210,87 @@ function Header() {
),
)}
</nav>
<a href="https://profen.openexplorer.xyz" target="_blank" rel="noopener noreferrer" className="hidden lg:block shrink-0" aria-label="Profen Engineering">
<span aria-hidden="true" className="hidden lg:block h-6 w-px bg-border" />
<a href="https://profen.openexplorer.xyz" target="_blank" rel="noopener noreferrer" className="shrink-0" aria-label="Profen Engineering">
<img src="/profen-logo.png" alt="Profen Engineering" className="h-9 w-auto" />
</a>
<button
onClick={() => setOpen((v) => !v)}
className="lg:hidden inline-flex items-center justify-center h-10 w-10 rounded-md border"
aria-label="Toggle menu"
</div>
</header>
{/* Drawer must be a sibling of <header>: the header's backdrop-blur creates a
containing block, which would trap a fixed-position drawer inside the 80px bar. */}
<MobileDrawer open={open} onClose={() => setOpen(false)} />
</>
);
}
function MobileDrawer({ open, onClose }: { open: boolean; onClose: () => void }) {
const closeButtonRef = useRef<HTMLButtonElement>(null);
// Lock body scroll while the drawer is open
useEffect(() => {
if (!open) return;
const previous = document.body.style.overflow;
document.body.style.overflow = "hidden";
return () => {
document.body.style.overflow = previous;
};
}, [open]);
// Close on Escape; move focus into the drawer when it opens
useEffect(() => {
if (!open) return;
closeButtonRef.current?.focus();
const onKeyDown = (event: KeyboardEvent) => {
if (event.key === "Escape") onClose();
};
document.addEventListener("keydown", onKeyDown);
return () => document.removeEventListener("keydown", onKeyDown);
}, [open, onClose]);
return (
<div
id="mobile-menu"
aria-hidden={!open}
inert={!open}
className={`fixed inset-0 z-[60] lg:hidden ${open ? "" : "pointer-events-none"}`}
>
{open ? <X className="h-5 w-5" /> : <Menu className="h-5 w-5" />}
{/* Backdrop */}
<div
onClick={onClose}
aria-hidden="true"
className={`absolute inset-0 bg-black/50 transition-opacity duration-300 ${
open ? "opacity-100" : "opacity-0"
}`}
/>
{/* Panel */}
<div
role="dialog"
aria-modal="true"
aria-label="Mobile navigation"
className={`absolute inset-y-0 left-0 flex w-80 max-w-[85vw] flex-col bg-background border-r shadow-xl transition-transform duration-300 ease-out ${
open ? "translate-x-0" : "-translate-x-full"
}`}
>
<div className="flex items-center justify-between border-b px-5 h-16 shrink-0">
<Link to="/" onClick={onClose} className="flex items-center">
<img src="/promist-logo.png" alt="Promist" className="h-9 w-auto" />
</Link>
<button
ref={closeButtonRef}
onClick={onClose}
className="inline-flex items-center justify-center h-9 w-9 rounded-md border hover:bg-muted transition-colors"
aria-label="Close menu"
>
<X className="h-4 w-4" />
</button>
</div>
{open && (
<div className="lg:hidden border-t">
<div className="container-page py-4 flex flex-col gap-3">
<nav className="flex-1 overflow-y-auto px-5 py-5 flex flex-col gap-4 text-base font-medium">
{NAV_ITEMS.map((item) => (
<MobileSubMenu key={item.label} item={item} onLinkClick={() => setOpen(false)} />
<MobileSubMenu key={item.label} item={item} onLinkClick={onClose} />
))}
</nav>
</div>
</div>
)}
</header>
);
}

View File

@@ -153,7 +153,7 @@ function HeroSlider() {
{/* Arrows */}
<button
onClick={() => emblaApi?.scrollPrev()}
className="absolute left-4 top-1/2 -translate-y-1/2 w-11 h-11 rounded-full bg-white/10 backdrop-blur-sm border border-white/20 flex items-center justify-center text-white hover:bg-white/20 transition-colors"
className="hidden sm:flex absolute left-4 top-1/2 -translate-y-1/2 w-11 h-11 rounded-full bg-white/10 backdrop-blur-sm border border-white/20 items-center justify-center text-white hover:bg-white/20 transition-colors"
aria-label="Previous slide"
>
<svg className="w-5 h-5" fill="none" viewBox="0 0 24 24" stroke="currentColor">
@@ -162,7 +162,7 @@ function HeroSlider() {
</button>
<button
onClick={() => emblaApi?.scrollNext()}
className="absolute right-4 top-1/2 -translate-y-1/2 w-11 h-11 rounded-full bg-white/10 backdrop-blur-sm border border-white/20 flex items-center justify-center text-white hover:bg-white/20 transition-colors"
className="hidden sm:flex absolute right-4 top-1/2 -translate-y-1/2 w-11 h-11 rounded-full bg-white/10 backdrop-blur-sm border border-white/20 items-center justify-center text-white hover:bg-white/20 transition-colors"
aria-label="Next slide"
>
<svg className="w-5 h-5" fill="none" viewBox="0 0 24 24" stroke="currentColor">