Barre de navigation mobile commune Groupe KA (KA Tabbar v1)
Pilule flottante fixe en bas (standard ka-ui/nav, version vanilla inline) : Découvrir · Recherche · Carte · Favoris, accent magenta signature, safe-area iOS, micro-interactions. Recherche = retour accueil + focus du champ central ; Carte = toutes les sorties sur la carte (drapeau state.mapAll one-shot dans isFiltering, remis à zéro à toute navigation hors tabbar). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
1 changed file +102 −1
modified
frontend/index.html
+102 −1
@@ -713,6 +713,48 @@ html.ka-menu-locked,html.ka-menu-locked body{overflow:hidden} | ||
| 713 | 713 | |
| 714 | 714 | #ssr{display:none} |
| 715 | 715 | .hidden{display:none!important} |
| 716 | + | |
| 717 | +/* ============================================================ | |
| 718 | + KA Tabbar v1 — barre de navigation mobile commune Groupe KA | |
| 719 | + (canonique : ka-ui.git/nav/ka-tabbar.css — accent magenta via | |
| 720 | + var(--accent) du site) ============================================ */ | |
| 721 | +.ka-tabbar{display:none} | |
| 722 | +@media (max-width:760px){ | |
| 723 | + .ka-tabbar{ | |
| 724 | + --ktb-bg:rgba(250,249,245,.94); | |
| 725 | + --ktb-ink:var(--ink,#16191a); | |
| 726 | + --ktb-dim:var(--ink-3,#70756d); | |
| 727 | + --ktb-accent:var(--accent,#d6336c); | |
| 728 | + --ktb-line:var(--hairline-strong,rgba(20,24,20,.16)); | |
| 729 | + --ktb-shadow:0 10px 30px rgba(20,24,20,.16),0 2px 6px rgba(20,24,20,.07); | |
| 730 | + position:fixed;left:max(12px,env(safe-area-inset-left));right:max(12px,env(safe-area-inset-right)); | |
| 731 | + bottom:calc(10px + env(safe-area-inset-bottom));z-index:var(--z-bottombar,600); | |
| 732 | + display:grid;grid-auto-flow:column;grid-auto-columns:1fr;max-width:440px;margin-inline:auto; | |
| 733 | + padding:5px;background:var(--ktb-bg); | |
| 734 | + -webkit-backdrop-filter:blur(14px) saturate(1.1);backdrop-filter:blur(14px) saturate(1.1); | |
| 735 | + border:1px solid var(--ktb-line);border-radius:18px;box-shadow:var(--ktb-shadow)} | |
| 736 | + .ka-tabbar a,.ka-tabbar button{appearance:none;background:none;border:0;cursor:pointer; | |
| 737 | + display:flex;flex-direction:column;align-items:center;justify-content:center;gap:3px; | |
| 738 | + min-height:50px;padding:6px 2px 5px;border-radius:13px; | |
| 739 | + font-family:var(--font-display,inherit);font-size:10px;font-weight:650;letter-spacing:.01em; | |
| 740 | + line-height:1;color:var(--ktb-dim);text-decoration:none;-webkit-tap-highlight-color:transparent; | |
| 741 | + transition:color .15s ease} | |
| 742 | + .ka-tabbar svg{display:block;transition:transform .15s ease} | |
| 743 | + .ka-tabbar a:active svg,.ka-tabbar button:active svg{transform:translateY(1px) scale(.9)} | |
| 744 | + .ka-tabbar .active{color:var(--ktb-active-label,var(--ktb-ink))} | |
| 745 | + .ka-tabbar .active svg{color:var(--ktb-accent);animation:ktb-pop .25s ease} | |
| 746 | + .ka-tabbar .active::after{content:"";width:16px;height:2.5px;border-radius:2px; | |
| 747 | + background:var(--ktb-accent);animation:ktb-under .22s ease} | |
| 748 | + body:has(.ka-tabbar){padding-bottom:calc(84px + env(safe-area-inset-bottom))} | |
| 749 | + body:has(.ka-tabbar) .kaa-btn.kaa-btn{bottom:calc(88px + env(safe-area-inset-bottom,0px));right:14px} | |
| 750 | + body:has(.ka-tabbar) .kaa-panel.kaa-panel:not(.kaa-full){bottom:calc(154px + env(safe-area-inset-bottom,0px))} | |
| 751 | +} | |
| 752 | +@keyframes ktb-under{from{transform:scaleX(0);opacity:0}} | |
| 753 | +@keyframes ktb-pop{40%{transform:translateY(-2px)}} | |
| 754 | +@media (prefers-reduced-motion:reduce){ | |
| 755 | + .ka-tabbar svg{transition:none} | |
| 756 | + .ka-tabbar .active svg,.ka-tabbar .active::after{animation:none} | |
| 757 | +} | |
| 716 | 758 | </style> |
| 717 | 759 | </head> |
| 718 | 760 | <body> |
@@ -1229,7 +1271,8 @@ function plusDays(n){ | ||
| 1229 | 1271 | } |
| 1230 | 1272 | function isFiltering(){ |
| 1231 | 1273 | return !!(state.q || state.region || state.category || state.free |
| 1232 | − || state.from || state.to || state.near); | |
| 1274 | + || state.from || state.to || state.near | |
| 1275 | + || state.mapAll); // onglet Carte (KA Tabbar) : résultats sans filtre | |
| 1233 | 1276 | } |
| 1234 | 1277 | function params(){ |
| 1235 | 1278 | const p = new URLSearchParams(); |
@@ -2773,5 +2816,63 @@ init(); | ||
| 2773 | 2816 | </script> |
| 2774 | 2817 | <script>window.KA_AGENT={site:"sorti-ka"}</script> |
| 2775 | 2818 | <script src="/ka-agent.js" defer></script> |
| 2819 | + | |
| 2820 | +<!-- ======== KA Tabbar v1 — navigation mobile commune Groupe KA (ka-ui/nav) ======== --> | |
| 2821 | +<nav class="ka-tabbar" id="ka-tabbar" aria-label="Navigation mobile"> | |
| 2822 | + <a data-nav data-tab="decouvrir" href="/"><svg width="21" height="21" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.7" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><circle cx="12" cy="12" r="9"/><path d="m15.5 8.5-2.2 5-4.8 2 2.2-5 4.8-2z"/></svg>Découvrir</a> | |
| 2823 | + <button type="button" data-tab="recherche" id="ktb-search"><svg width="21" height="21" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.7" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><circle cx="11" cy="11" r="7"/><path d="m20.5 20.5-4.2-4.2"/></svg>Recherche</button> | |
| 2824 | + <button type="button" data-tab="carte" id="ktb-carte"><svg width="21" height="21" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.7" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M12 21s-7-5.6-7-11a7 7 0 0 1 14 0c0 5.4-7 11-7 11z"/><circle cx="12" cy="10" r="2.6"/></svg>Carte</button> | |
| 2825 | + <a data-nav data-tab="favoris" href="/favoris"><svg width="21" height="21" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.7" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M12 20.5S4.5 15.8 2.6 11.4A5.3 5.3 0 0 1 12 6.6a5.3 5.3 0 0 1 9.4 4.8C19.5 15.8 12 20.5 12 20.5z"/></svg>Favoris</a> | |
| 2826 | +</nav> | |
| 2827 | +<script> | |
| 2828 | +/* KA Tabbar : liaisons (routeur maison data-nav + actions locales) */ | |
| 2829 | +(function(){ | |
| 2830 | + const bar = document.getElementById("ka-tabbar"); | |
| 2831 | + bindNav(bar); // les <a data-nav> passent par le routeur SPA | |
| 2832 | + | |
| 2833 | + function ktbSync(){ | |
| 2834 | + const p = location.pathname; | |
| 2835 | + const mapOn = !$("results").classList.contains("hidden") | |
| 2836 | + && !$("mapwrap").classList.contains("hidden"); | |
| 2837 | + let k = null; | |
| 2838 | + if(p === "/favoris") k = "favoris"; | |
| 2839 | + else if(p === "/" || p.startsWith("/region/") || p.startsWith("/categorie/") | |
| 2840 | + || p.startsWith("/evenement/")) k = mapOn ? "carte" : "decouvrir"; | |
| 2841 | + bar.querySelectorAll("[data-tab]").forEach(b => | |
| 2842 | + b.classList.toggle("active", b.dataset.tab === k)); | |
| 2843 | + } | |
| 2844 | + | |
| 2845 | + /* Recherche : accueil + focus du champ central */ | |
| 2846 | + $("ktb-search").onclick = () => { | |
| 2847 | + const wasMap = state.mapAll; | |
| 2848 | + state.mapAll = false; | |
| 2849 | + if(location.pathname !== "/") go("/"); | |
| 2850 | + else if(wasMap) load(); | |
| 2851 | + window.scrollTo({top:0, behavior:"smooth"}); | |
| 2852 | + setTimeout(() => $("q").focus({preventScroll:true}), 60); | |
| 2853 | + setTimeout(ktbSync, 80); | |
| 2854 | + }; | |
| 2855 | + | |
| 2856 | + /* Carte : toutes les sorties sur la carte (vue résultats forcée) */ | |
| 2857 | + $("ktb-carte").onclick = () => { | |
| 2858 | + state.mapAll = true; | |
| 2859 | + if(location.pathname !== "/") go("/"); | |
| 2860 | + else load(); | |
| 2861 | + setView("map"); | |
| 2862 | + setTimeout(() => $("results").scrollIntoView({behavior:"smooth", block:"start"}), 120); | |
| 2863 | + setTimeout(ktbSync, 150); | |
| 2864 | + }; | |
| 2865 | + | |
| 2866 | + /* toute navigation hors tabbar sort du mode carte-de-tout (one-shot) */ | |
| 2867 | + document.addEventListener("click", e => { | |
| 2868 | + const a = e.target.closest && e.target.closest("a[data-nav]"); | |
| 2869 | + if(a && !bar.contains(a)) state.mapAll = false; | |
| 2870 | + setTimeout(ktbSync, 80); | |
| 2871 | + }, true); | |
| 2872 | + window.addEventListener("popstate", () => { state.mapAll = false; setTimeout(ktbSync, 80); }); | |
| 2873 | + | |
| 2874 | + ktbSync(); | |
| 2875 | +})(); | |
| 2876 | +</script> | |
| 2776 | 2877 | </body> |
| 2777 | 2878 | </html> |
| 2778 | 2879 | |