Commit
Mobile : socle mobile tokens.css injecté, champs ≥16 px jusqu au breakpoint tabbar, logo tactile, ka-agent corrigé
- frontend/src/ka/tokens.css : socle mobile obligatoire (échelle z-index --z-*, --vh100 dvh, .ka-bottombar, .ka-scroll-lock, .ka-menu/.ka-overlay, filet anti-zoom iOS 16 px, zone tactile étendue du gk-badge) — inliné au build. - frontend/src/index.template.html : champs de recherche/filtres (.search-input input, select.f) ≥16 px étendus de ≤760 px à ≤940 px (breakpoint tabbar) ; zone tactile ≥44 px du logo .brand (::after inset -8px, rendu inchangé). - frontend/src/ka-agent.js : safe-areas, textarea 16 px, plein écran ≤640 px + verrou kaa-lock — recopié dans dist/ par build_frontend.py. - Vérifié : les boutons d en-tête masqués ≤940 px restent tous joignables — Statistiques et Explorer l annuaire via la tabbar basse, Contact via la nav du footer local ; le bouton compte KA ID reste visible dans le header. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
modified
frontend/dist/index.html
+179 −0
@@ -74,7 +74,24 @@ | |||
| 74 | 74 | ||
| 75 | /* Cible tactile minimale */ | 75 | /* Cible tactile minimale */ |
| 76 | --touch: 44px; | 76 | --touch: 44px; |
| 77 | + | ||
| 78 | + /* ---- Échelle z-index COMMUNE (obligatoire — aucune valeur arbitraire) ---- | ||
| 79 | + grain de film body::before = 9999 (pointer-events:none, toujours au-dessus, | ||
| 80 | + purement visuel). Tout composant interactif se place sous 9999 : */ | ||
| 81 | + --z-content: 1; /* contenu positionné ordinaire */ | ||
| 82 | + --z-sticky: 300; /* éléments sticky de contenu (sous-nav…) */ | ||
| 83 | + --z-header: 500; /* header du site */ | ||
| 84 | + --z-bottombar: 600; /* tab bar / barre d'action basse */ | ||
| 85 | + --z-dropdown: 700; /* menus déroulants (au-dessus des barres) */ | ||
| 86 | + --z-overlay: 800; /* voile sous modale/panneau */ | ||
| 87 | + --z-modal: 900; /* modales, panneaux de filtres, galeries */ | ||
| 88 | + --z-toast: 950; /* notifications */ | ||
| 89 | + | ||
| 90 | + /* ---- Hauteur de viewport DYNAMIQUE (barre du navigateur mobile qui se | ||
| 91 | + replie au scroll) : toujours var(--vh100), JAMAIS 100vh en dur. ---- */ | ||
| 92 | + --vh100: 100vh; | ||
| 77 | } | 93 | } |
| 94 | +@supports (height: 100dvh) { :root { --vh100: 100dvh; } } | ||
| 78 | 95 | ||
| 79 | /* ---- Socle ---- */ | 96 | /* ---- Socle ---- */ |
| 80 | * { box-sizing: border-box; } | 97 | * { box-sizing: border-box; } |
@@ -232,6 +249,76 @@ body > * { position: relative; } | |||
| 232 | /* ---- Utilitaires responsive ---- */ | 249 | /* ---- Utilitaires responsive ---- */ |
| 233 | .only-mobile { display: initial; } .only-desktop { display: none; } | 250 | .only-mobile { display: initial; } .only-desktop { display: none; } |
| 234 | @media (min-width: 768px) { .only-mobile { display: none; } .only-desktop { display: initial; } } | 251 | @media (min-width: 768px) { .only-mobile { display: none; } .only-desktop { display: initial; } } |
| 252 | + | ||
| 253 | +/* ============================================================================= | ||
| 254 | + SOCLE MOBILE OBLIGATOIRE (2026-08-19) — règles communes aux 13 sites. | ||
| 255 | + RÈGLES pour tout composant futur : | ||
| 256 | + · TAP, jamais :hover, pour ouvrir un menu (le survol n'existe pas au doigt) ; | ||
| 257 | + le :hover ne sert qu'aux effets décoratifs, sous @media (hover: hover). | ||
| 258 | + · var(--vh100) (dvh) et JAMAIS 100vh pour tout élément calé sur le viewport. | ||
| 259 | + · position:fixed exige qu'AUCUN ancêtre n'ait transform/filter/perspective/ | ||
| 260 | + will-change/backdrop-filter — sinon monter l'élément à la racine (body). | ||
| 261 | + · Barre basse fixe = .ka-bottombar + classe has-bottombar sur <body>. | ||
| 262 | + · Menu/panneau ouvert = .ka-scroll-lock sur <html> (scroll arrière-plan gelé). | ||
| 263 | + · z-index : uniquement l'échelle --z-* ci-dessus. | ||
| 264 | + · Zones tactiles ≥ var(--touch) (44 px), champs ≥ 16 px (zoom iOS). | ||
| 265 | + ========================================================================== */ | ||
| 266 | + | ||
| 267 | +/* Anti-zoom iOS : au doigt, aucun champ sous 16 px (le focus d'un champ <16 px | ||
| 268 | + déclenche un zoom automatique de page sur Safari iOS). !important assumé : | ||
| 269 | + c'est un filet d'accessibilité, un champ plus petit casse le zoom quoi | ||
| 270 | + qu'il arrive — ne s'applique qu'aux écrans tactiles. */ | ||
| 271 | +@media (hover: none) and (pointer: coarse) { | ||
| 272 | + input:not([type="checkbox"]):not([type="radio"]):not([type="range"]), | ||
| 273 | + select, textarea { font-size: max(16px, 1em) !important; } | ||
| 274 | +} | ||
| 275 | + | ||
| 276 | +/* Zone tactile du badge Groupe KA étendue à ≥44 px au doigt, sans changer | ||
| 277 | + son rendu (débord de la zone cliquable via pseudo-élément). */ | ||
| 278 | +@media (pointer: coarse) { | ||
| 279 | + .gk-badge { position: relative; } | ||
| 280 | + .gk-badge::after { content: ""; position: absolute; inset: -8px; } | ||
| 281 | +} | ||
| 282 | + | ||
| 283 | +/* Hauteurs viewport dynamiques */ | ||
| 284 | +.h-viewport { height: 100vh; height: 100dvh; } | ||
| 285 | +.min-h-viewport { min-height: 100vh; min-height: 100dvh; } | ||
| 286 | + | ||
| 287 | +/* Barre fixe basse fiable : safe-area iPhone incluse, immunisée contre les | ||
| 288 | + transforms accidentels, et compensation d'espace via body.has-bottombar. */ | ||
| 289 | +.ka-bottombar { | ||
| 290 | + position: fixed; left: 0; right: 0; bottom: 0; | ||
| 291 | + z-index: var(--z-bottombar); | ||
| 292 | + padding-bottom: env(safe-area-inset-bottom, 0px); | ||
| 293 | + background: var(--surface); | ||
| 294 | + border-top: 1.5px solid var(--ink); | ||
| 295 | + transform: none !important; filter: none !important; | ||
| 296 | +} | ||
| 297 | +body.has-bottombar { padding-bottom: calc(var(--bottombar-h, 64px) + env(safe-area-inset-bottom, 0px)); } | ||
| 298 | + | ||
| 299 | +/* Verrou de scroll d'arrière-plan (menu mobile, modale, panneau de filtres | ||
| 300 | + ouverts) : ajouter/retirer .ka-scroll-lock sur <html>. */ | ||
| 301 | +html.ka-scroll-lock, html.ka-scroll-lock body { overflow: hidden !important; overscroll-behavior: none; } | ||
| 302 | + | ||
| 303 | +/* Menu déroulant de référence : au-dessus de tout contenu (cartes Mapbox | ||
| 304 | + incluses), défilement interne avec élan, jamais plus haut que l'écran. */ | ||
| 305 | +.ka-menu { | ||
| 306 | + position: absolute; z-index: var(--z-dropdown); | ||
| 307 | + min-width: 180px; | ||
| 308 | + max-height: min(60vh, 420px); max-height: min(60dvh, 420px); | ||
| 309 | + overflow-y: auto; -webkit-overflow-scrolling: touch; overscroll-behavior: contain; | ||
| 310 | + background: var(--surface); border: 1.5px solid var(--ink); | ||
| 311 | + border-radius: var(--r-card); box-shadow: var(--shadow-off-mid); | ||
| 312 | +} | ||
| 313 | +.ka-menu a, .ka-menu button, .ka-menu label, .ka-menu [role="menuitem"], .ka-menu [role="option"] { | ||
| 314 | + display: flex; align-items: center; min-height: var(--touch); | ||
| 315 | + padding: 10px 14px; width: 100%; text-decoration: none; | ||
| 316 | +} | ||
| 317 | +/* Voile plein écran sous une modale / un panneau */ | ||
| 318 | +.ka-overlay { | ||
| 319 | + position: fixed; inset: 0; z-index: var(--z-overlay); | ||
| 320 | + background: rgba(20, 24, 20, 0.45); | ||
| 321 | +} | ||
| 235 | (ka/tokens.css — design system commun Groupe KA, | 322 | (ka/tokens.css — design system commun Groupe KA, |
| 236 | importé EN PREMIER) et {"org":{"name":"Groupe KA","legalName":"Groupe KA — Simon-Pierre Boucher","tagline":"Holding québécois d'agrégateurs de produits et services entièrement automatisés.","disclaimer":"Groupe KA est un agrégateur de contenu : nous ne vendons rien, ne louons rien et ne sommes partie à aucune transaction.","copyrightHolder":"Groupe KA — Simon-Pierre Boucher"},"hub":{"url":"https://www.groupe-ka.com","loginPath":"/connexion","signupNote":"La création de compte KA ID se fait sur le hub groupe-ka.com ; chaque site délègue sa connexion via /api/auth/ka/login."},"contacts":[{"email":"contact@groupe-ka.com","role":"Projets, partenariats & données"},{"email":"info@groupe-ka.com","role":"Médias & questions générales"},{"email":"admin@groupe-ka.com","role":"Légal, vie privée & Loi 25"}],"legal":[{"label":"Conditions d'utilisation","href":"https://www.groupe-ka.com/conditions"},{"label":"Politique de confidentialité","href":"https://www.groupe-ka.com/confidentialite"},{"label":"Protection des renseignements personnels (Loi 25)","href":"https://www.groupe-ka.com/loi-25"},{"label":"Transparence des robots d'indexation","href":"https://www.groupe-ka.com/bots"}],"sites":[{"id":"groupe-ka","wordmark":"Groupe KA","domain":"www.groupe-ka.com","accent":"#d9f26b","accentSoft":"#f0f9d2","accentDeep":"#123f2e","onAccent":"#141814","tagline":"Le portail de l'écosystème ·Ka"},{"id":"trouve-ka","wordmark":"Trouve·Ka","domain":"www.trouve-ka.com","accent":"#1c7ed6","accentSoft":"#e7f2fd","accentDeep":"#14508f","onAccent":"#ffffff","tagline":"Le moteur de recherche du web québécois"},{"id":"lou-ka","wordmark":"Lou·Ka","domain":"www.lou-ka.com","accent":"#ff6a00","accentSoft":"#fff1e6","accentDeep":"#cc5500","onAccent":"#ffffff","tagline":"Tous les logements à louer"},{"id":"immo-ka","wordmark":"Immo·Ka","domain":"www.immo-ka.com","accent":"#e23744","accentSoft":"#fbe0e2","accentDeep":"#a8232e","onAccent":"#ffffff","tagline":"Toutes les propriétés à vendre"},{"id":"vrai-prix","wordmark":"Vrai-Prix","domain":"www.vrai-prix.com","accent":"#ff5148","accentSoft":"#ffe3e0","accentDeep":"#9e2a25","onAccent":"#ffffff","tagline":"La valeur réelle de chaque propriété"},{"id":"auto-ka","wordmark":"Auto·Ka","domain":"www.auto-ka.com","accent":"#ff5a2a","accentSoft":"#ffe8de","accentDeep":"#cc3f16","onAccent":"#ffffff","tagline":"Les voitures usagées du Québec"},{"id":"fabri-ka","wordmark":"Fabri·Ka","domain":"www.fabri-ka.com","accent":"#c4532e","accentSoft":"#f7e3da","accentDeep":"#a94525","onAccent":"#ffffff","tagline":"Les produits fabriqués au Québec"},{"id":"food-ka","wordmark":"Food·Ka","domain":"www.food-ka.com","accent":"#1f9d55","accentSoft":"#e2f5ea","accentDeep":"#157a40","onAccent":"#ffffff","tagline":"Les prix d'épicerie, suivis à la source"},{"id":"resto-ka","wordmark":"Resto·Ka","domain":"www.resto-ka.com","accent":"#f08c00","accentSoft":"#fdeed7","accentDeep":"#b96a00","onAccent":"#141814","tagline":"Chaque resto, chaque plat, chaque prix"},{"id":"sorti-ka","wordmark":"Sorti·Ka","domain":"www.sorti-ka.com","accent":"#d6336c","accentSoft":"#fbe0eb","accentDeep":"#a12551","onAccent":"#ffffff","tagline":"Toutes les sorties, dans les 17 régions"},{"id":"crea-ka","wordmark":"Créa·Ka","domain":"www.crea-ka.com","accent":"#7048e8","accentSoft":"#ece5fc","accentDeep":"#5433b8","onAccent":"#ffffff","tagline":"Les créateurs d'ici, tous leurs liens"},{"id":"api-ka","wordmark":"API·Ka","domain":"www.api-ka.com","accent":"#3b5bdb","accentSoft":"#e4eafb","accentDeep":"#2b44a8","onAccent":"#ffffff","tagline":"La donnée de l'écosystème, par API"},{"id":"job-ka","wordmark":"Job·Ka","domain":"www.job-ka.com","accent":"#0c8599","accentSoft":"#def0f4","accentDeep":"#095c6b","onAccent":"#ffffff","tagline":"Tous les emplois des employeurs québécois"}],"extraFooterLinks":[{"label":"ValoPlex","href":"https://www.valoplex.com"},{"label":"Ka2","href":"https://www.ka2.bot"},{"label":"Ka4","href":"https://www.ka4.bot"},{"label":"Ka6","href":"https://www.ka6.bot"}]} (ka/ecosystem.json — contacts, | 323 | importé EN PREMIER) et {"org":{"name":"Groupe KA","legalName":"Groupe KA — Simon-Pierre Boucher","tagline":"Holding québécois d'agrégateurs de produits et services entièrement automatisés.","disclaimer":"Groupe KA est un agrégateur de contenu : nous ne vendons rien, ne louons rien et ne sommes partie à aucune transaction.","copyrightHolder":"Groupe KA — Simon-Pierre Boucher"},"hub":{"url":"https://www.groupe-ka.com","loginPath":"/connexion","signupNote":"La création de compte KA ID se fait sur le hub groupe-ka.com ; chaque site délègue sa connexion via /api/auth/ka/login."},"contacts":[{"email":"contact@groupe-ka.com","role":"Projets, partenariats & données"},{"email":"info@groupe-ka.com","role":"Médias & questions générales"},{"email":"admin@groupe-ka.com","role":"Légal, vie privée & Loi 25"}],"legal":[{"label":"Conditions d'utilisation","href":"https://www.groupe-ka.com/conditions"},{"label":"Politique de confidentialité","href":"https://www.groupe-ka.com/confidentialite"},{"label":"Protection des renseignements personnels (Loi 25)","href":"https://www.groupe-ka.com/loi-25"},{"label":"Transparence des robots d'indexation","href":"https://www.groupe-ka.com/bots"}],"sites":[{"id":"groupe-ka","wordmark":"Groupe KA","domain":"www.groupe-ka.com","accent":"#d9f26b","accentSoft":"#f0f9d2","accentDeep":"#123f2e","onAccent":"#141814","tagline":"Le portail de l'écosystème ·Ka"},{"id":"trouve-ka","wordmark":"Trouve·Ka","domain":"www.trouve-ka.com","accent":"#1c7ed6","accentSoft":"#e7f2fd","accentDeep":"#14508f","onAccent":"#ffffff","tagline":"Le moteur de recherche du web québécois"},{"id":"lou-ka","wordmark":"Lou·Ka","domain":"www.lou-ka.com","accent":"#ff6a00","accentSoft":"#fff1e6","accentDeep":"#cc5500","onAccent":"#ffffff","tagline":"Tous les logements à louer"},{"id":"immo-ka","wordmark":"Immo·Ka","domain":"www.immo-ka.com","accent":"#e23744","accentSoft":"#fbe0e2","accentDeep":"#a8232e","onAccent":"#ffffff","tagline":"Toutes les propriétés à vendre"},{"id":"vrai-prix","wordmark":"Vrai-Prix","domain":"www.vrai-prix.com","accent":"#ff5148","accentSoft":"#ffe3e0","accentDeep":"#9e2a25","onAccent":"#ffffff","tagline":"La valeur réelle de chaque propriété"},{"id":"auto-ka","wordmark":"Auto·Ka","domain":"www.auto-ka.com","accent":"#ff5a2a","accentSoft":"#ffe8de","accentDeep":"#cc3f16","onAccent":"#ffffff","tagline":"Les voitures usagées du Québec"},{"id":"fabri-ka","wordmark":"Fabri·Ka","domain":"www.fabri-ka.com","accent":"#c4532e","accentSoft":"#f7e3da","accentDeep":"#a94525","onAccent":"#ffffff","tagline":"Les produits fabriqués au Québec"},{"id":"food-ka","wordmark":"Food·Ka","domain":"www.food-ka.com","accent":"#1f9d55","accentSoft":"#e2f5ea","accentDeep":"#157a40","onAccent":"#ffffff","tagline":"Les prix d'épicerie, suivis à la source"},{"id":"resto-ka","wordmark":"Resto·Ka","domain":"www.resto-ka.com","accent":"#f08c00","accentSoft":"#fdeed7","accentDeep":"#b96a00","onAccent":"#141814","tagline":"Chaque resto, chaque plat, chaque prix"},{"id":"sorti-ka","wordmark":"Sorti·Ka","domain":"www.sorti-ka.com","accent":"#d6336c","accentSoft":"#fbe0eb","accentDeep":"#a12551","onAccent":"#ffffff","tagline":"Toutes les sorties, dans les 17 régions"},{"id":"crea-ka","wordmark":"Créa·Ka","domain":"www.crea-ka.com","accent":"#7048e8","accentSoft":"#ece5fc","accentDeep":"#5433b8","onAccent":"#ffffff","tagline":"Les créateurs d'ici, tous leurs liens"},{"id":"api-ka","wordmark":"API·Ka","domain":"www.api-ka.com","accent":"#3b5bdb","accentSoft":"#e4eafb","accentDeep":"#2b44a8","onAccent":"#ffffff","tagline":"La donnée de l'écosystème, par API"},{"id":"job-ka","wordmark":"Job·Ka","domain":"www.job-ka.com","accent":"#0c8599","accentSoft":"#def0f4","accentDeep":"#095c6b","onAccent":"#ffffff","tagline":"Tous les emplois des employeurs québécois"}],"extraFooterLinks":[{"label":"ValoPlex","href":"https://www.valoplex.com"},{"label":"Ka2","href":"https://www.ka2.bot"},{"label":"Ka4","href":"https://www.ka4.bot"},{"label":"Ka6","href":"https://www.ka6.bot"}]} (ka/ecosystem.json — contacts, |
| 237 | sites, mentions légales du groupe : footer commun + page /contact). | 324 | sites, mentions légales du groupe : footer commun + page /contact). |
@@ -337,7 +424,24 @@ body > * { position: relative; } | |||
| 337 | 424 | ||
| 338 | /* Cible tactile minimale */ | 425 | /* Cible tactile minimale */ |
| 339 | --touch: 44px; | 426 | --touch: 44px; |
| 427 | + | ||
| 428 | + /* ---- Échelle z-index COMMUNE (obligatoire — aucune valeur arbitraire) ---- | ||
| 429 | + grain de film body::before = 9999 (pointer-events:none, toujours au-dessus, | ||
| 430 | + purement visuel). Tout composant interactif se place sous 9999 : */ | ||
| 431 | + --z-content: 1; /* contenu positionné ordinaire */ | ||
| 432 | + --z-sticky: 300; /* éléments sticky de contenu (sous-nav…) */ | ||
| 433 | + --z-header: 500; /* header du site */ | ||
| 434 | + --z-bottombar: 600; /* tab bar / barre d'action basse */ | ||
| 435 | + --z-dropdown: 700; /* menus déroulants (au-dessus des barres) */ | ||
| 436 | + --z-overlay: 800; /* voile sous modale/panneau */ | ||
| 437 | + --z-modal: 900; /* modales, panneaux de filtres, galeries */ | ||
| 438 | + --z-toast: 950; /* notifications */ | ||
| 439 | + | ||
| 440 | + /* ---- Hauteur de viewport DYNAMIQUE (barre du navigateur mobile qui se | ||
| 441 | + replie au scroll) : toujours var(--vh100), JAMAIS 100vh en dur. ---- */ | ||
| 442 | + --vh100: 100vh; | ||
| 340 | } | 443 | } |
| 444 | +@supports (height: 100dvh) { :root { --vh100: 100dvh; } } | ||
| 341 | 445 | ||
| 342 | /* ---- Socle ---- */ | 446 | /* ---- Socle ---- */ |
| 343 | * { box-sizing: border-box; } | 447 | * { box-sizing: border-box; } |
@@ -496,6 +600,76 @@ body > * { position: relative; } | |||
| 496 | .only-mobile { display: initial; } .only-desktop { display: none; } | 600 | .only-mobile { display: initial; } .only-desktop { display: none; } |
| 497 | @media (min-width: 768px) { .only-mobile { display: none; } .only-desktop { display: initial; } } | 601 | @media (min-width: 768px) { .only-mobile { display: none; } .only-desktop { display: initial; } } |
| 498 | 602 | ||
| 603 | +/* ============================================================================= | ||
| 604 | + SOCLE MOBILE OBLIGATOIRE (2026-08-19) — règles communes aux 13 sites. | ||
| 605 | + RÈGLES pour tout composant futur : | ||
| 606 | + · TAP, jamais :hover, pour ouvrir un menu (le survol n'existe pas au doigt) ; | ||
| 607 | + le :hover ne sert qu'aux effets décoratifs, sous @media (hover: hover). | ||
| 608 | + · var(--vh100) (dvh) et JAMAIS 100vh pour tout élément calé sur le viewport. | ||
| 609 | + · position:fixed exige qu'AUCUN ancêtre n'ait transform/filter/perspective/ | ||
| 610 | + will-change/backdrop-filter — sinon monter l'élément à la racine (body). | ||
| 611 | + · Barre basse fixe = .ka-bottombar + classe has-bottombar sur <body>. | ||
| 612 | + · Menu/panneau ouvert = .ka-scroll-lock sur <html> (scroll arrière-plan gelé). | ||
| 613 | + · z-index : uniquement l'échelle --z-* ci-dessus. | ||
| 614 | + · Zones tactiles ≥ var(--touch) (44 px), champs ≥ 16 px (zoom iOS). | ||
| 615 | + ========================================================================== */ | ||
| 616 | + | ||
| 617 | +/* Anti-zoom iOS : au doigt, aucun champ sous 16 px (le focus d'un champ <16 px | ||
| 618 | + déclenche un zoom automatique de page sur Safari iOS). !important assumé : | ||
| 619 | + c'est un filet d'accessibilité, un champ plus petit casse le zoom quoi | ||
| 620 | + qu'il arrive — ne s'applique qu'aux écrans tactiles. */ | ||
| 621 | +@media (hover: none) and (pointer: coarse) { | ||
| 622 | + input:not([type="checkbox"]):not([type="radio"]):not([type="range"]), | ||
| 623 | + select, textarea { font-size: max(16px, 1em) !important; } | ||
| 624 | +} | ||
| 625 | + | ||
| 626 | +/* Zone tactile du badge Groupe KA étendue à ≥44 px au doigt, sans changer | ||
| 627 | + son rendu (débord de la zone cliquable via pseudo-élément). */ | ||
| 628 | +@media (pointer: coarse) { | ||
| 629 | + .gk-badge { position: relative; } | ||
| 630 | + .gk-badge::after { content: ""; position: absolute; inset: -8px; } | ||
| 631 | +} | ||
| 632 | + | ||
| 633 | +/* Hauteurs viewport dynamiques */ | ||
| 634 | +.h-viewport { height: 100vh; height: 100dvh; } | ||
| 635 | +.min-h-viewport { min-height: 100vh; min-height: 100dvh; } | ||
| 636 | + | ||
| 637 | +/* Barre fixe basse fiable : safe-area iPhone incluse, immunisée contre les | ||
| 638 | + transforms accidentels, et compensation d'espace via body.has-bottombar. */ | ||
| 639 | +.ka-bottombar { | ||
| 640 | + position: fixed; left: 0; right: 0; bottom: 0; | ||
| 641 | + z-index: var(--z-bottombar); | ||
| 642 | + padding-bottom: env(safe-area-inset-bottom, 0px); | ||
| 643 | + background: var(--surface); | ||
| 644 | + border-top: 1.5px solid var(--ink); | ||
| 645 | + transform: none !important; filter: none !important; | ||
| 646 | +} | ||
| 647 | +body.has-bottombar { padding-bottom: calc(var(--bottombar-h, 64px) + env(safe-area-inset-bottom, 0px)); } | ||
| 648 | + | ||
| 649 | +/* Verrou de scroll d'arrière-plan (menu mobile, modale, panneau de filtres | ||
| 650 | + ouverts) : ajouter/retirer .ka-scroll-lock sur <html>. */ | ||
| 651 | +html.ka-scroll-lock, html.ka-scroll-lock body { overflow: hidden !important; overscroll-behavior: none; } | ||
| 652 | + | ||
| 653 | +/* Menu déroulant de référence : au-dessus de tout contenu (cartes Mapbox | ||
| 654 | + incluses), défilement interne avec élan, jamais plus haut que l'écran. */ | ||
| 655 | +.ka-menu { | ||
| 656 | + position: absolute; z-index: var(--z-dropdown); | ||
| 657 | + min-width: 180px; | ||
| 658 | + max-height: min(60vh, 420px); max-height: min(60dvh, 420px); | ||
| 659 | + overflow-y: auto; -webkit-overflow-scrolling: touch; overscroll-behavior: contain; | ||
| 660 | + background: var(--surface); border: 1.5px solid var(--ink); | ||
| 661 | + border-radius: var(--r-card); box-shadow: var(--shadow-off-mid); | ||
| 662 | +} | ||
| 663 | +.ka-menu a, .ka-menu button, .ka-menu label, .ka-menu [role="menuitem"], .ka-menu [role="option"] { | ||
| 664 | + display: flex; align-items: center; min-height: var(--touch); | ||
| 665 | + padding: 10px 14px; width: 100%; text-decoration: none; | ||
| 666 | +} | ||
| 667 | +/* Voile plein écran sous une modale / un panneau */ | ||
| 668 | +.ka-overlay { | ||
| 669 | + position: fixed; inset: 0; z-index: var(--z-overlay); | ||
| 670 | + background: rgba(20, 24, 20, 0.45); | ||
| 671 | +} | ||
| 672 | + | ||
| 499 | </style> | 673 | </style> |
| 500 | <style> | 674 | <style> |
| 501 | /* Surcharges Créa-Ka par-dessus ka/tokens.css. Convention du groupe : les | 675 | /* Surcharges Créa-Ka par-dessus ka/tokens.css. Convention du groupe : les |
@@ -636,6 +810,11 @@ select.f.active{background-color:var(--accent-soft);color:var(--ink);border-colo | |||
| 636 | .filters::-webkit-scrollbar{display:none} | 810 | .filters::-webkit-scrollbar{display:none} |
| 637 | select.f{flex:none;font-size:16px;padding:8px 30px 8px 13px} | 811 | select.f{flex:none;font-size:16px;padding:8px 30px 8px 13px} |
| 638 | } | 812 | } |
| 813 | +/* Anti-zoom iOS jusqu'au breakpoint tabbar (940 px) : champs >= 16 px | ||
| 814 | + (filet global : tokens.css force aussi 16 px au doigt) */ | ||
| 815 | +@media(max-width:940px){.search-input input,select.f{font-size:16px}} | ||
| 816 | +/* Cible tactile >= 44 px du logo (112×26 rendus) : zone étendue invisible */ | ||
| 817 | +@media(pointer:coarse){.brand{position:relative}.brand::after{content:"";position:absolute;inset:-8px}} | ||
| 639 | 818 | ||
| 640 | /* ===== grille créateurs ===== */ | 819 | /* ===== grille créateurs ===== */ |
| 641 | .grid{display:grid;grid-template-columns:repeat(auto-fill,minmax(250px,1fr));gap:18px;padding:24px 0 10px} | 820 | .grid{display:grid;grid-template-columns:repeat(auto-fill,minmax(250px,1fr));gap:18px;padding:24px 0 10px} |
modified
frontend/dist/ka-agent.js
+25 −7
@@ -17,18 +17,19 @@ | |||
| 17 | 17 | ||
| 18 | /* ---------- styles ---------- */ | 18 | /* ---------- styles ---------- */ |
| 19 | var css = ` | 19 | var css = ` |
| 20 | − .kaa-btn{position:fixed;right:18px;bottom:18px;z-index:2147483000;width:58px;height:58px; | 20 | + .kaa-btn{position:fixed;right:18px;bottom:calc(18px + env(safe-area-inset-bottom,0px));z-index:2147483000;width:58px;height:58px; |
| 21 | border-radius:50%;border:2px solid var(--ink,#141814);background:var(--accent,#d9f26b); | 21 | border-radius:50%;border:2px solid var(--ink,#141814);background:var(--accent,#d9f26b); |
| 22 | color:var(--on-accent,#141814);font:700 17px/1 "Space Grotesk",system-ui,sans-serif; | 22 | color:var(--on-accent,#141814);font:700 17px/1 "Space Grotesk",system-ui,sans-serif; |
| 23 | cursor:pointer;box-shadow:4px 4px 0 rgba(20,24,20,.85);display:flex;align-items:center; | 23 | cursor:pointer;box-shadow:4px 4px 0 rgba(20,24,20,.85);display:flex;align-items:center; |
| 24 | − justify-content:center;transition:transform .15s;padding-bottom:env(safe-area-inset-bottom,0)} | 24 | + justify-content:center;transition:transform .15s} |
| 25 | .kaa-btn:hover{transform:translate(-2px,-2px)} | 25 | .kaa-btn:hover{transform:translate(-2px,-2px)} |
| 26 | − .kaa-panel{position:fixed;right:16px;bottom:86px;z-index:2147483001;width:min(392px,calc(100vw - 24px)); | 26 | + .kaa-panel{position:fixed;right:16px;bottom:calc(86px + env(safe-area-inset-bottom,0px));z-index:2147483001;width:min(392px,calc(100vw - 24px)); |
| 27 | − height:min(580px,calc(100dvh - 110px));display:none;flex-direction:column;background:var(--paper,#f5f3ee); | 27 | + height:min(580px,calc(100vh - 110px));height:min(580px,calc(100dvh - 110px));display:none;flex-direction:column;background:var(--paper,#f5f3ee); |
| 28 | border:2px solid var(--ink,#141814);border-radius:14px;box-shadow:8px 8px 0 rgba(20,24,20,.85); | 28 | border:2px solid var(--ink,#141814);border-radius:14px;box-shadow:8px 8px 0 rgba(20,24,20,.85); |
| 29 | overflow:hidden;font-family:Inter,system-ui,sans-serif} | 29 | overflow:hidden;font-family:Inter,system-ui,sans-serif} |
| 30 | .kaa-panel.kaa-open{display:flex} | 30 | .kaa-panel.kaa-open{display:flex} |
| 31 | − .kaa-panel.kaa-full{right:0;bottom:0;width:100vw;height:100dvh;border-radius:0;border:0;box-shadow:none} | 31 | + .kaa-panel.kaa-full{right:0;bottom:0;width:100vw;height:100vh;height:100dvh;border-radius:0;border:0;box-shadow:none} |
| 32 | + html.kaa-lock,html.kaa-lock body{overflow:hidden!important;overscroll-behavior:none} | ||
| 32 | .kaa-head{display:flex;align-items:center;gap:9px;padding:11px 14px;background:var(--ink,#141814); | 33 | .kaa-head{display:flex;align-items:center;gap:9px;padding:11px 14px;background:var(--ink,#141814); |
| 33 | color:var(--paper,#f5f3ee);flex:none} | 34 | color:var(--paper,#f5f3ee);flex:none} |
| 34 | .kaa-head b{font:700 15px "Space Grotesk",system-ui,sans-serif;letter-spacing:-.02em} | 35 | .kaa-head b{font:700 15px "Space Grotesk",system-ui,sans-serif;letter-spacing:-.02em} |
@@ -53,7 +54,7 @@ | |||
| 53 | border-radius:999px;background:var(--surface-2,#faf9f5)} | 54 | border-radius:999px;background:var(--surface-2,#faf9f5)} |
| 54 | .kaa-in{display:flex;gap:8px;padding:11px;border-top:2px solid var(--ink,#141814);background:#fff;flex:none; | 55 | .kaa-in{display:flex;gap:8px;padding:11px;border-top:2px solid var(--ink,#141814);background:#fff;flex:none; |
| 55 | padding-bottom:calc(11px + env(safe-area-inset-bottom,0))} | 56 | padding-bottom:calc(11px + env(safe-area-inset-bottom,0))} |
| 56 | − .kaa-in textarea{flex:1;resize:none;min-height:44px;max-height:110px;padding:11px 12px;font:13.5px/1.4 Inter,system-ui,sans-serif; | 57 | + .kaa-in textarea{flex:1;resize:none;min-height:44px;max-height:110px;padding:10px 12px;font:16px/1.4 Inter,system-ui,sans-serif; |
| 57 | border:1.5px solid var(--ink,#141814);border-radius:9px;background:var(--paper,#f5f3ee);outline:none} | 58 | border:1.5px solid var(--ink,#141814);border-radius:9px;background:var(--paper,#f5f3ee);outline:none} |
| 58 | .kaa-in button{min-width:52px;min-height:44px;border:1.5px solid var(--ink,#141814);border-radius:9px; | 59 | .kaa-in button{min-width:52px;min-height:44px;border:1.5px solid var(--ink,#141814);border-radius:9px; |
| 59 | background:var(--ink,#141814);color:var(--accent,#d9f26b);font:700 16px "Space Grotesk",system-ui;cursor:pointer} | 60 | background:var(--ink,#141814);color:var(--accent,#d9f26b);font:700 16px "Space Grotesk",system-ui;cursor:pointer} |
@@ -142,15 +143,32 @@ | |||
| 142 | } | 143 | } |
| 143 | 144 | ||
| 144 | /* ---------- ouverture / plein écran ---------- */ | 145 | /* ---------- ouverture / plein écran ---------- */ |
| 146 | + /* Sur téléphone : panneau plein écran d'office + scroll d'arrière-plan | ||
| 147 | + verrouillé tant qu'il est ouvert (html.kaa-lock). Pas de focus clavier | ||
| 148 | + forcé au tap (le clavier ne s'ouvre que si l'utilisateur touche le champ). */ | ||
| 149 | + var mobileMq = window.matchMedia("(max-width: 640px)"); | ||
| 150 | + var fineInput = window.matchMedia("(hover: hover) and (pointer: fine)"); | ||
| 151 | + function syncLock() { | ||
| 152 | + var open = panel.classList.contains("kaa-open"); | ||
| 153 | + if (open && mobileMq.matches) panel.classList.add("kaa-full"); | ||
| 154 | + document.documentElement.classList.toggle( | ||
| 155 | + "kaa-lock", open && panel.classList.contains("kaa-full")); | ||
| 156 | + } | ||
| 145 | btn.addEventListener("click", function () { | 157 | btn.addEventListener("click", function () { |
| 146 | panel.classList.toggle("kaa-open"); | 158 | panel.classList.toggle("kaa-open"); |
| 147 | − if (panel.classList.contains("kaa-open")) { render(); ta.focus(); } | 159 | + if (panel.classList.contains("kaa-open")) { |
| 160 | + render(); | ||
| 161 | + if (fineInput.matches) ta.focus(); | ||
| 162 | + } | ||
| 163 | + syncLock(); | ||
| 148 | }); | 164 | }); |
| 149 | panel.querySelector(".kaa-x2").addEventListener("click", function () { | 165 | panel.querySelector(".kaa-x2").addEventListener("click", function () { |
| 150 | panel.classList.remove("kaa-open", "kaa-full"); | 166 | panel.classList.remove("kaa-open", "kaa-full"); |
| 167 | + syncLock(); | ||
| 151 | }); | 168 | }); |
| 152 | panel.querySelector(".kaa-x1").addEventListener("click", function () { | 169 | panel.querySelector(".kaa-x1").addEventListener("click", function () { |
| 153 | panel.classList.toggle("kaa-full"); | 170 | panel.classList.toggle("kaa-full"); |
| 171 | + syncLock(); | ||
| 154 | scroll(); | 172 | scroll(); |
| 155 | }); | 173 | }); |
| 156 | 174 | ||
modified
frontend/src/index.template.html
+5 −0
@@ -178,6 +178,11 @@ select.f.active{background-color:var(--accent-soft);color:var(--ink);border-colo | |||
| 178 | .filters::-webkit-scrollbar{display:none} | 178 | .filters::-webkit-scrollbar{display:none} |
| 179 | select.f{flex:none;font-size:16px;padding:8px 30px 8px 13px} | 179 | select.f{flex:none;font-size:16px;padding:8px 30px 8px 13px} |
| 180 | } | 180 | } |
| 181 | +/* Anti-zoom iOS jusqu'au breakpoint tabbar (940 px) : champs >= 16 px | ||
| 182 | + (filet global : tokens.css force aussi 16 px au doigt) */ | ||
| 183 | +@media(max-width:940px){.search-input input,select.f{font-size:16px}} | ||
| 184 | +/* Cible tactile >= 44 px du logo (112×26 rendus) : zone étendue invisible */ | ||
| 185 | +@media(pointer:coarse){.brand{position:relative}.brand::after{content:"";position:absolute;inset:-8px}} | ||
| 181 | 186 | ||
| 182 | /* ===== grille créateurs ===== */ | 187 | /* ===== grille créateurs ===== */ |
| 183 | .grid{display:grid;grid-template-columns:repeat(auto-fill,minmax(250px,1fr));gap:18px;padding:24px 0 10px} | 188 | .grid{display:grid;grid-template-columns:repeat(auto-fill,minmax(250px,1fr));gap:18px;padding:24px 0 10px} |
modified
frontend/src/ka-agent.js
+25 −7
@@ -17,18 +17,19 @@ | |||
| 17 | 17 | ||
| 18 | /* ---------- styles ---------- */ | 18 | /* ---------- styles ---------- */ |
| 19 | var css = ` | 19 | var css = ` |
| 20 | − .kaa-btn{position:fixed;right:18px;bottom:18px;z-index:2147483000;width:58px;height:58px; | 20 | + .kaa-btn{position:fixed;right:18px;bottom:calc(18px + env(safe-area-inset-bottom,0px));z-index:2147483000;width:58px;height:58px; |
| 21 | border-radius:50%;border:2px solid var(--ink,#141814);background:var(--accent,#d9f26b); | 21 | border-radius:50%;border:2px solid var(--ink,#141814);background:var(--accent,#d9f26b); |
| 22 | color:var(--on-accent,#141814);font:700 17px/1 "Space Grotesk",system-ui,sans-serif; | 22 | color:var(--on-accent,#141814);font:700 17px/1 "Space Grotesk",system-ui,sans-serif; |
| 23 | cursor:pointer;box-shadow:4px 4px 0 rgba(20,24,20,.85);display:flex;align-items:center; | 23 | cursor:pointer;box-shadow:4px 4px 0 rgba(20,24,20,.85);display:flex;align-items:center; |
| 24 | − justify-content:center;transition:transform .15s;padding-bottom:env(safe-area-inset-bottom,0)} | 24 | + justify-content:center;transition:transform .15s} |
| 25 | .kaa-btn:hover{transform:translate(-2px,-2px)} | 25 | .kaa-btn:hover{transform:translate(-2px,-2px)} |
| 26 | − .kaa-panel{position:fixed;right:16px;bottom:86px;z-index:2147483001;width:min(392px,calc(100vw - 24px)); | 26 | + .kaa-panel{position:fixed;right:16px;bottom:calc(86px + env(safe-area-inset-bottom,0px));z-index:2147483001;width:min(392px,calc(100vw - 24px)); |
| 27 | − height:min(580px,calc(100dvh - 110px));display:none;flex-direction:column;background:var(--paper,#f5f3ee); | 27 | + height:min(580px,calc(100vh - 110px));height:min(580px,calc(100dvh - 110px));display:none;flex-direction:column;background:var(--paper,#f5f3ee); |
| 28 | border:2px solid var(--ink,#141814);border-radius:14px;box-shadow:8px 8px 0 rgba(20,24,20,.85); | 28 | border:2px solid var(--ink,#141814);border-radius:14px;box-shadow:8px 8px 0 rgba(20,24,20,.85); |
| 29 | overflow:hidden;font-family:Inter,system-ui,sans-serif} | 29 | overflow:hidden;font-family:Inter,system-ui,sans-serif} |
| 30 | .kaa-panel.kaa-open{display:flex} | 30 | .kaa-panel.kaa-open{display:flex} |
| 31 | − .kaa-panel.kaa-full{right:0;bottom:0;width:100vw;height:100dvh;border-radius:0;border:0;box-shadow:none} | 31 | + .kaa-panel.kaa-full{right:0;bottom:0;width:100vw;height:100vh;height:100dvh;border-radius:0;border:0;box-shadow:none} |
| 32 | + html.kaa-lock,html.kaa-lock body{overflow:hidden!important;overscroll-behavior:none} | ||
| 32 | .kaa-head{display:flex;align-items:center;gap:9px;padding:11px 14px;background:var(--ink,#141814); | 33 | .kaa-head{display:flex;align-items:center;gap:9px;padding:11px 14px;background:var(--ink,#141814); |
| 33 | color:var(--paper,#f5f3ee);flex:none} | 34 | color:var(--paper,#f5f3ee);flex:none} |
| 34 | .kaa-head b{font:700 15px "Space Grotesk",system-ui,sans-serif;letter-spacing:-.02em} | 35 | .kaa-head b{font:700 15px "Space Grotesk",system-ui,sans-serif;letter-spacing:-.02em} |
@@ -53,7 +54,7 @@ | |||
| 53 | border-radius:999px;background:var(--surface-2,#faf9f5)} | 54 | border-radius:999px;background:var(--surface-2,#faf9f5)} |
| 54 | .kaa-in{display:flex;gap:8px;padding:11px;border-top:2px solid var(--ink,#141814);background:#fff;flex:none; | 55 | .kaa-in{display:flex;gap:8px;padding:11px;border-top:2px solid var(--ink,#141814);background:#fff;flex:none; |
| 55 | padding-bottom:calc(11px + env(safe-area-inset-bottom,0))} | 56 | padding-bottom:calc(11px + env(safe-area-inset-bottom,0))} |
| 56 | − .kaa-in textarea{flex:1;resize:none;min-height:44px;max-height:110px;padding:11px 12px;font:13.5px/1.4 Inter,system-ui,sans-serif; | 57 | + .kaa-in textarea{flex:1;resize:none;min-height:44px;max-height:110px;padding:10px 12px;font:16px/1.4 Inter,system-ui,sans-serif; |
| 57 | border:1.5px solid var(--ink,#141814);border-radius:9px;background:var(--paper,#f5f3ee);outline:none} | 58 | border:1.5px solid var(--ink,#141814);border-radius:9px;background:var(--paper,#f5f3ee);outline:none} |
| 58 | .kaa-in button{min-width:52px;min-height:44px;border:1.5px solid var(--ink,#141814);border-radius:9px; | 59 | .kaa-in button{min-width:52px;min-height:44px;border:1.5px solid var(--ink,#141814);border-radius:9px; |
| 59 | background:var(--ink,#141814);color:var(--accent,#d9f26b);font:700 16px "Space Grotesk",system-ui;cursor:pointer} | 60 | background:var(--ink,#141814);color:var(--accent,#d9f26b);font:700 16px "Space Grotesk",system-ui;cursor:pointer} |
@@ -142,15 +143,32 @@ | |||
| 142 | } | 143 | } |
| 143 | 144 | ||
| 144 | /* ---------- ouverture / plein écran ---------- */ | 145 | /* ---------- ouverture / plein écran ---------- */ |
| 146 | + /* Sur téléphone : panneau plein écran d'office + scroll d'arrière-plan | ||
| 147 | + verrouillé tant qu'il est ouvert (html.kaa-lock). Pas de focus clavier | ||
| 148 | + forcé au tap (le clavier ne s'ouvre que si l'utilisateur touche le champ). */ | ||
| 149 | + var mobileMq = window.matchMedia("(max-width: 640px)"); | ||
| 150 | + var fineInput = window.matchMedia("(hover: hover) and (pointer: fine)"); | ||
| 151 | + function syncLock() { | ||
| 152 | + var open = panel.classList.contains("kaa-open"); | ||
| 153 | + if (open && mobileMq.matches) panel.classList.add("kaa-full"); | ||
| 154 | + document.documentElement.classList.toggle( | ||
| 155 | + "kaa-lock", open && panel.classList.contains("kaa-full")); | ||
| 156 | + } | ||
| 145 | btn.addEventListener("click", function () { | 157 | btn.addEventListener("click", function () { |
| 146 | panel.classList.toggle("kaa-open"); | 158 | panel.classList.toggle("kaa-open"); |
| 147 | − if (panel.classList.contains("kaa-open")) { render(); ta.focus(); } | 159 | + if (panel.classList.contains("kaa-open")) { |
| 160 | + render(); | ||
| 161 | + if (fineInput.matches) ta.focus(); | ||
| 162 | + } | ||
| 163 | + syncLock(); | ||
| 148 | }); | 164 | }); |
| 149 | panel.querySelector(".kaa-x2").addEventListener("click", function () { | 165 | panel.querySelector(".kaa-x2").addEventListener("click", function () { |
| 150 | panel.classList.remove("kaa-open", "kaa-full"); | 166 | panel.classList.remove("kaa-open", "kaa-full"); |
| 167 | + syncLock(); | ||
| 151 | }); | 168 | }); |
| 152 | panel.querySelector(".kaa-x1").addEventListener("click", function () { | 169 | panel.querySelector(".kaa-x1").addEventListener("click", function () { |
| 153 | panel.classList.toggle("kaa-full"); | 170 | panel.classList.toggle("kaa-full"); |
| 171 | + syncLock(); | ||
| 154 | scroll(); | 172 | scroll(); |
| 155 | }); | 173 | }); |
| 156 | 174 | ||
modified
frontend/src/ka/tokens.css
+87 −0
@@ -69,7 +69,24 @@ | |||
| 69 | 69 | ||
| 70 | /* Cible tactile minimale */ | 70 | /* Cible tactile minimale */ |
| 71 | --touch: 44px; | 71 | --touch: 44px; |
| 72 | + | ||
| 73 | + /* ---- Échelle z-index COMMUNE (obligatoire — aucune valeur arbitraire) ---- | ||
| 74 | + grain de film body::before = 9999 (pointer-events:none, toujours au-dessus, | ||
| 75 | + purement visuel). Tout composant interactif se place sous 9999 : */ | ||
| 76 | + --z-content: 1; /* contenu positionné ordinaire */ | ||
| 77 | + --z-sticky: 300; /* éléments sticky de contenu (sous-nav…) */ | ||
| 78 | + --z-header: 500; /* header du site */ | ||
| 79 | + --z-bottombar: 600; /* tab bar / barre d'action basse */ | ||
| 80 | + --z-dropdown: 700; /* menus déroulants (au-dessus des barres) */ | ||
| 81 | + --z-overlay: 800; /* voile sous modale/panneau */ | ||
| 82 | + --z-modal: 900; /* modales, panneaux de filtres, galeries */ | ||
| 83 | + --z-toast: 950; /* notifications */ | ||
| 84 | + | ||
| 85 | + /* ---- Hauteur de viewport DYNAMIQUE (barre du navigateur mobile qui se | ||
| 86 | + replie au scroll) : toujours var(--vh100), JAMAIS 100vh en dur. ---- */ | ||
| 87 | + --vh100: 100vh; | ||
| 72 | } | 88 | } |
| 89 | +@supports (height: 100dvh) { :root { --vh100: 100dvh; } } | ||
| 73 | 90 | ||
| 74 | /* ---- Socle ---- */ | 91 | /* ---- Socle ---- */ |
| 75 | * { box-sizing: border-box; } | 92 | * { box-sizing: border-box; } |
@@ -227,3 +244,73 @@ body > * { position: relative; } | |||
| 227 | /* ---- Utilitaires responsive ---- */ | 244 | /* ---- Utilitaires responsive ---- */ |
| 228 | .only-mobile { display: initial; } .only-desktop { display: none; } | 245 | .only-mobile { display: initial; } .only-desktop { display: none; } |
| 229 | @media (min-width: 768px) { .only-mobile { display: none; } .only-desktop { display: initial; } } | 246 | @media (min-width: 768px) { .only-mobile { display: none; } .only-desktop { display: initial; } } |
| 247 | + | ||
| 248 | +/* ============================================================================= | ||
| 249 | + SOCLE MOBILE OBLIGATOIRE (2026-08-19) — règles communes aux 13 sites. | ||
| 250 | + RÈGLES pour tout composant futur : | ||
| 251 | + · TAP, jamais :hover, pour ouvrir un menu (le survol n'existe pas au doigt) ; | ||
| 252 | + le :hover ne sert qu'aux effets décoratifs, sous @media (hover: hover). | ||
| 253 | + · var(--vh100) (dvh) et JAMAIS 100vh pour tout élément calé sur le viewport. | ||
| 254 | + · position:fixed exige qu'AUCUN ancêtre n'ait transform/filter/perspective/ | ||
| 255 | + will-change/backdrop-filter — sinon monter l'élément à la racine (body). | ||
| 256 | + · Barre basse fixe = .ka-bottombar + classe has-bottombar sur <body>. | ||
| 257 | + · Menu/panneau ouvert = .ka-scroll-lock sur <html> (scroll arrière-plan gelé). | ||
| 258 | + · z-index : uniquement l'échelle --z-* ci-dessus. | ||
| 259 | + · Zones tactiles ≥ var(--touch) (44 px), champs ≥ 16 px (zoom iOS). | ||
| 260 | + ========================================================================== */ | ||
| 261 | + | ||
| 262 | +/* Anti-zoom iOS : au doigt, aucun champ sous 16 px (le focus d'un champ <16 px | ||
| 263 | + déclenche un zoom automatique de page sur Safari iOS). !important assumé : | ||
| 264 | + c'est un filet d'accessibilité, un champ plus petit casse le zoom quoi | ||
| 265 | + qu'il arrive — ne s'applique qu'aux écrans tactiles. */ | ||
| 266 | +@media (hover: none) and (pointer: coarse) { | ||
| 267 | + input:not([type="checkbox"]):not([type="radio"]):not([type="range"]), | ||
| 268 | + select, textarea { font-size: max(16px, 1em) !important; } | ||
| 269 | +} | ||
| 270 | + | ||
| 271 | +/* Zone tactile du badge Groupe KA étendue à ≥44 px au doigt, sans changer | ||
| 272 | + son rendu (débord de la zone cliquable via pseudo-élément). */ | ||
| 273 | +@media (pointer: coarse) { | ||
| 274 | + .gk-badge { position: relative; } | ||
| 275 | + .gk-badge::after { content: ""; position: absolute; inset: -8px; } | ||
| 276 | +} | ||
| 277 | + | ||
| 278 | +/* Hauteurs viewport dynamiques */ | ||
| 279 | +.h-viewport { height: 100vh; height: 100dvh; } | ||
| 280 | +.min-h-viewport { min-height: 100vh; min-height: 100dvh; } | ||
| 281 | + | ||
| 282 | +/* Barre fixe basse fiable : safe-area iPhone incluse, immunisée contre les | ||
| 283 | + transforms accidentels, et compensation d'espace via body.has-bottombar. */ | ||
| 284 | +.ka-bottombar { | ||
| 285 | + position: fixed; left: 0; right: 0; bottom: 0; | ||
| 286 | + z-index: var(--z-bottombar); | ||
| 287 | + padding-bottom: env(safe-area-inset-bottom, 0px); | ||
| 288 | + background: var(--surface); | ||
| 289 | + border-top: 1.5px solid var(--ink); | ||
| 290 | + transform: none !important; filter: none !important; | ||
| 291 | +} | ||
| 292 | +body.has-bottombar { padding-bottom: calc(var(--bottombar-h, 64px) + env(safe-area-inset-bottom, 0px)); } | ||
| 293 | + | ||
| 294 | +/* Verrou de scroll d'arrière-plan (menu mobile, modale, panneau de filtres | ||
| 295 | + ouverts) : ajouter/retirer .ka-scroll-lock sur <html>. */ | ||
| 296 | +html.ka-scroll-lock, html.ka-scroll-lock body { overflow: hidden !important; overscroll-behavior: none; } | ||
| 297 | + | ||
| 298 | +/* Menu déroulant de référence : au-dessus de tout contenu (cartes Mapbox | ||
| 299 | + incluses), défilement interne avec élan, jamais plus haut que l'écran. */ | ||
| 300 | +.ka-menu { | ||
| 301 | + position: absolute; z-index: var(--z-dropdown); | ||
| 302 | + min-width: 180px; | ||
| 303 | + max-height: min(60vh, 420px); max-height: min(60dvh, 420px); | ||
| 304 | + overflow-y: auto; -webkit-overflow-scrolling: touch; overscroll-behavior: contain; | ||
| 305 | + background: var(--surface); border: 1.5px solid var(--ink); | ||
| 306 | + border-radius: var(--r-card); box-shadow: var(--shadow-off-mid); | ||
| 307 | +} | ||
| 308 | +.ka-menu a, .ka-menu button, .ka-menu label, .ka-menu [role="menuitem"], .ka-menu [role="option"] { | ||
| 309 | + display: flex; align-items: center; min-height: var(--touch); | ||
| 310 | + padding: 10px 14px; width: 100%; text-decoration: none; | ||
| 311 | +} | ||
| 312 | +/* Voile plein écran sous une modale / un panneau */ | ||
| 313 | +.ka-overlay { | ||
| 314 | + position: fixed; inset: 0; z-index: var(--z-overlay); | ||
| 315 | + background: rgba(20, 24, 20, 0.45); | ||
| 316 | +} | ||
| 230 | 317 | ||