SPB Git

spb/vrai-prix

Public

Vrai-Prix — l'évaluation du vrai prix des propriétés résidentielles au Québec.

6.4 KB · 164 lines tsx
1// Auteur : Simon-Pierre Boucher — contact@spboucher.ai2/**3 * Navigation d'en-tête. Le menu mobile utilise le motif checkbox + peer-checked :4 * il s'ouvre et se ferme en PUR CSS — fonctionne même sans JavaScript (cache5 * périmé, hydratation cassée, JS bloqué). Le JS n'est qu'une amélioration6 * (fermeture au changement de page, verrou de défilement).7 */8"use client";9import { useEffect, useRef } from "react";10import Link from "next/link";11import { usePathname } from "next/navigation";12import { useLang } from "./LangContext";13import GroupeKaBadge from "../ka/GroupeKaBadge";1415export default function HeaderNav() {16  const { lang, setLang } = useLang();17  const path = usePathname();18  const fr = lang === "fr";19  const box = useRef<HTMLInputElement>(null);2021  // améliorations JS (facultatives) : fermer à la navigation, verrouiller le scroll22  useEffect(() => {23    if (box.current) box.current.checked = false;24    document.body.style.overflow = "";25  }, [path]);26  const onToggle = () => {27    document.body.style.overflow = box.current?.checked ? "hidden" : "";28  };2930  const items = [31    { href: "/", label: fr ? "Estimer" : "Estimate" },32    { href: "/carte", label: fr ? "Carte" : "Map" },33    { href: "/ka", label: fr ? "Ka · agent IA" : "Ka · AI agent" },34    { href: "/parc", label: fr ? "Parc immobilier" : "Portfolio" },35    { href: "/stats", label: fr ? "Statistiques" : "Statistics" },36    { href: "/methodologie", label: fr ? "Méthodologie" : "Methodology" },37  ];38  const isActive = (href: string) =>39    href === "/" ? path === "/" : path.startsWith(href);4041  return (42    <>43      {/* nav texte — desktop : soulignement rouge = page courante */}44      <nav className="hidden items-center gap-5 lg:flex">45        {items.map((it) => (46          <Link47            key={it.href}48            href={it.href}49            className={`vp-mono inline-flex min-h-[38px] items-center border-b-2 pt-[2px] text-[11px] font-medium uppercase tracking-[0.1em] transition-colors ${50              isActive(it.href)51                ? "border-accent text-ink"52                : "border-transparent text-ink-2 hover:text-ink"53            }`}54          >55            {it.label}56          </Link>57        ))}58      </nav>5960      {/* ---- menu mobile : checkbox cachée + peer-checked, zéro JS requis ---- */}61      <div className="lg:hidden">62        <input63          ref={box}64          type="checkbox"65          id="vp-menu"66          className="peer sr-only"67          onChange={onToggle}68          aria-label="Menu"69        />70        {/* hamburger (label = bouton) */}71        <label72          htmlFor="vp-menu"73          aria-hidden="true"74          className="flex h-11 w-11 cursor-pointer flex-col items-center justify-center gap-[5px]"75        >76          <span className="h-px w-[20px] bg-ink" />77          <span className="h-px w-[20px] bg-ink" />78          <span className="h-px w-[12px] self-center ml-[8px] bg-accent" />79        </label>8081        {/* panneau plein écran encre — sibling du checkbox, affiché via peer-checked */}82        <div83          className="vp-dark fixed inset-0 z-[var(--z-modal,900)] hidden flex-col overflow-y-auto peer-checked:flex"84          role="dialog"85          aria-modal="true"86          aria-label="Menu"87        >88          {/* barre du haut : marque + fermer */}89          <div className="flex h-14 shrink-0 items-center justify-between border-b border-[rgba(245,243,238,0.18)] px-4">90            <Link91              href="/"92              className="vp-display flex items-baseline gap-1.5 text-[19px] font-bold leading-none tracking-[-0.04em] text-[var(--paper-on-ink)]"93            >94              Vrai&nbsp;Prix95              <span96                aria-hidden="true"97                className="inline-block h-[7px] w-[7px] bg-[var(--accent-bright)]"98              />99            </Link>100            <label101              htmlFor="vp-menu"102              aria-label={fr ? "Fermer le menu" : "Close menu"}103              className="vp-mono flex h-11 w-11 cursor-pointer items-center justify-center text-[15px] text-[var(--paper-on-ink)]"104            >105              ✕106            </label>107          </div>108109          {/* liens géants */}110          <nav className="flex flex-1 flex-col justify-center px-6 py-8">111            {items.map((it, i) => (112              <Link113                key={it.href}114                href={it.href}115                className="group flex items-baseline gap-4 border-b border-[rgba(245,243,238,0.12)] py-[18px] last:border-0"116              >117                <span className="vp-mono text-[11px] font-medium text-[var(--accent-bright)]">118                  0{i + 1}119                </span>120                <span121                  className={`vp-display text-[28px] font-bold uppercase leading-none tracking-[-0.03em] ${122                    isActive(it.href)123                      ? "text-[var(--accent-bright)]"124                      : "text-[var(--paper-on-ink)] group-active:text-[var(--accent-bright)]"125                  }`}126                >127                  {it.label}128                </span>129                {isActive(it.href) && (130                  <span131                    aria-hidden="true"132                    className="mb-[2px] inline-block h-[6px] w-[6px] self-center bg-[var(--accent-bright)]"133                  />134                )}135              </Link>136            ))}137          </nav>138139          {/* pied du menu */}140          <div className="shrink-0 border-t border-[rgba(245,243,238,0.18)] px-6 pb-[max(20px,env(safe-area-inset-bottom))] pt-4">141            {/* entrée « Un service Groupe KA » du menu mobile */}142            <div className="mb-4">143              <GroupeKaBadge />144            </div>145            <button146              onClick={() => setLang(fr ? "en" : "fr")}147              className="vp-mono mb-4 border-b border-[var(--paper-on-ink)] pb-1 text-[12px] font-medium uppercase tracking-[0.1em] text-[var(--paper-on-ink)]"148            >149              {fr ? "English version" : "Version française"}150            </button>151            <div className="vp-mono flex flex-wrap gap-x-5 gap-y-1.5 text-[10.5px] uppercase tracking-[0.06em] text-[rgba(245,243,238,0.55)]">152              <Link href="/conditions">{fr ? "Conditions" : "Terms"}</Link>153              <Link href="/confidentialite">154                {fr ? "Confidentialité" : "Privacy"}155              </Link>156              <Link href="/contact">Contact</Link>157            </div>158          </div>159        </div>160      </div>161    </>162  );163}164
Files · af7f80f1c7c2cd80e86ffcdeb194836bf51a5bef
Menu

Theme