SPB Git

spb/ka-ui

Public

nav/ : règle ✕-dans-le-panneau + fix-mm-close.py (correctif 2026-08-25)

Le bouton de fermeture doit vivre dans le panneau fixed (jamais seulement le
burger du header, hors écran quand le header n'est pas épinglé en bas de page).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Simon-Pierre Boucher committed (Aug 25, 2026) parent 36b608e Browse files

2 changed files +66 −0

modified nav/NAV-STANDARD.md +6 −0
@@ -28,6 +28,12 @@ d'un site à l'autre.
28 28
29 ## Règles 29 ## Règles
30 - JAMAIS de dropdown `position:absolute` ancré au header pour le menu mobile. 30 - JAMAIS de dropdown `position:absolute` ancré au header pour le menu mobile.
31+- **Le ✕ de fermeture DOIT vivre DANS le panneau** (barre interne ou bouton
32+ `position:fixed` en haut à droite), jamais uniquement dans le header : si le
33+ header n'est pas épinglé (bas de page, sticky cassé par un ancêtre overflow),
34+ le burger→✕ défile hors écran alors que le panneau fixed reste affiché
35+ (bug corrigé le 2026-08-25 sur les 6 apps React + trouve-ka : `.mm-close`
36+ fixed + burger `visibility:hidden` à l'ouverture — une seule commande).
31 - Le hamburger et le ✕ font ≥44×44px (cible tactile). 37 - Le hamburger et le ✕ font ≥44×44px (cible tactile).
32 - Les liens du panneau reprennent EXACTEMENT les liens de la nav desktop (+ liens 38 - Les liens du panneau reprennent EXACTEMENT les liens de la nav desktop (+ liens
33 secondaires du footer si pertinent), i18n fr-CA. 39 secondaires du footer si pertinent), i18n fr-CA.
added nav/fix-mm-close.py +60 −0
@@ -0,0 +1,60 @@
1+#!/usr/bin/env python3
2+"""KA Nav v2 — correctif ✕ (2026-08-25) : le bouton de fermeture vivait dans le
3+header (burger→✕) ; si le header n'est pas épinglé (bas de page), il est hors
4+écran alors que le panneau fixed est visible. On met un ✕ FIXE dans le panneau
5+et on masque le burger à l'ouverture (une seule commande de fermeture).
6+Usage : fix-mm-close.py <racine app> <has|menu-open>"""
7+import sys
8+
9+root, variant = sys.argv[1], sys.argv[2]
10+tsx_path = f"{root}/frontend/src/App.tsx"
11+css_path = f"{root}/frontend/src/styles.css"
12+
13+# --- App.tsx : ✕ premier enfant du panneau -------------------------------
14+tsx = open(tsx_path).read()
15+if "mm-close" in tsx:
16+ print("App.tsx: deja fait")
17+else:
18+ anchor = '<div className={`mobile-menu ${open ? "open" : ""}`} role="navigation" aria-label="Menu mobile">'
19+ assert anchor in tsx, "ancre mobile-menu introuvable"
20+ btn = anchor + (
21+ "\n {/* ✕ fixe du panneau : visible peu importe le scroll,"
22+ " le burger du header est masqué à l'ouverture */}"
23+ '\n <button type="button" className="mm-close" aria-label="Fermer le menu"'
24+ "\n onClick={() => setOpen(false)}>✕</button>"
25+ )
26+ tsx = tsx.replace(anchor, btn, 1)
27+ open(tsx_path, "w").write(tsx)
28+ print("App.tsx: OK")
29+
30+# --- styles.css : burger masqué à l'ouverture + style du ✕ ----------------
31+css = open(css_path).read()
32+old_comment = "/* le rang du header (marque + burger devenu X) reste au-dessus du panneau */\n"
33+old_has = old_comment + ".header:has(.mobile-menu.open) .header-inner { position: relative; z-index: 60; }"
34+old_mo = old_comment + ".header.menu-open .header-inner { position: relative; z-index: 60; }"
35+hide = ("/* burger masque a l'ouverture : la fermeture passe par le X fixe du panneau */\n"
36+ + (".header:has(.mobile-menu.open) .menu-btn { visibility: hidden; }"
37+ if variant == "has" else
38+ ".header.menu-open .menu-btn { visibility: hidden; }"))
39+mm_close = hide + """
40+/* bouton de fermeture du panneau : fixe en haut a droite du viewport */
41+.mm-close {
42+ position: fixed; top: 10px; right: 14px; z-index: 2;
43+ width: 44px; height: 44px; display: flex; align-items: center; justify-content: center;
44+ border: 1.5px solid var(--ink); border-radius: 999px;
45+ background: var(--ink); color: var(--paper);
46+ font-size: 17px; font-weight: 700; line-height: 1; cursor: pointer; padding: 0;
47+}"""
48+done = False
49+for old in (old_has, old_mo):
50+ if old in css:
51+ css = css.replace(old, mm_close, 1)
52+ done = True
53+ break
54+if not done and ".mm-close" in css:
55+ print("styles.css: deja fait")
56+elif not done:
57+ raise SystemExit("regle header-inner z-60 introuvable")
58+else:
59+ open(css_path, "w").write(css)
60+ print("styles.css: OK")
61
Menu

Theme