From 4c6d8779213e3e7b0257d594c70bfb21f37570b0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc-Andr=C3=A9=20Bombeck?= Date: Sat, 16 May 2026 01:42:12 +0200 Subject: [PATCH] refactor(coach): consolidate launch button to inline + layout-FAB shape Lift the mobile FAB out of into a new mounted once next to in the Insights layout. Each sub-page previously mounted which rendered BOTH the FAB and the inline pill, so a phone user ended up with 2-3 duplicate FAB nodes in the a11y tree (collapsed visually by fixed positioning). The inline pill stays on the per-page action rows where copy + position matter; the FAB now paints exactly once per Insights surface. --- src/app/insights/layout.tsx | 8 ++ .../insights/coach-launch-button.tsx | 77 +++++++------------ src/components/insights/layout-coach-fab.tsx | 51 ++++++++++++ 3 files changed, 87 insertions(+), 49 deletions(-) create mode 100644 src/components/insights/layout-coach-fab.tsx diff --git a/src/app/insights/layout.tsx b/src/app/insights/layout.tsx index 25b0b4cb7..25ba61cb7 100644 --- a/src/app/insights/layout.tsx +++ b/src/app/insights/layout.tsx @@ -1,6 +1,7 @@ import type { ReactNode } from "react"; import { InsightsLayoutShell } from "@/components/insights/insights-layout-shell"; +import { LayoutCoachFab } from "@/components/insights/layout-coach-fab"; import { LayoutCoachMount } from "@/components/insights/layout-coach-mount"; import { CoachLaunchProvider } from "@/lib/insights/coach-launch-context"; @@ -20,11 +21,18 @@ import { CoachLaunchProvider } from "@/lib/insights/coach-launch-context"; * strip + suggested-prompt chips. Decision F (audit MA3) drove the * promotion: a mobile user who navigates from `/insights` to * `/insights/blutdruck` keeps a one-tap path back into the Coach. + * + * v1.4.28 R3c — the mobile FAB now also lives here via + * `` (carved out of `` so each sub- + * page mount no longer duplicates the FAB into the a11y tree). The + * inline `` pill stays on the per-page action rows + * where copy + position matter. */ export default function InsightsLayout({ children }: { children: ReactNode }) { return ( {children} + ); diff --git a/src/components/insights/coach-launch-button.tsx b/src/components/insights/coach-launch-button.tsx index 69454539c..f559d097e 100644 --- a/src/components/insights/coach-launch-button.tsx +++ b/src/components/insights/coach-launch-button.tsx @@ -8,14 +8,17 @@ import { useTranslations } from "@/lib/i18n/context"; import { useCoachLaunch } from "@/lib/insights/coach-launch-context"; /** - * v1.4.27 R3d MB4 — Coach launch button. + * Inline desktop Coach launch button. * - * Decision F: every routed Insights sub-page mounts this button so the - * Coach drawer (mounted at the layout root) is always reachable. The - * button renders as a sticky-bottom FAB on `` therefore painted a + * second FAB into the DOM, collapsing to one visible button only + * because of `fixed` positioning — the duplicates still landed in the + * a11y tree. The FAB now lives once at the layout level (see + * `` in `src/app/insights/layout.tsx`); this component + * keeps only the inline `lg+` pill that sub-pages mount inside their + * action rows. * * The button is a thin wrapper around `useCoachLaunch()` — it only * renders when the context provider is mounted (so dropping the button @@ -46,48 +49,24 @@ export function CoachLaunchButton({ const accessibleLabel = label ?? t("insights.heroActionAskCoach"); + // Inline header-style action on `lg+`. Hidden below `lg` because the + // layout-level FAB (``) covers that breakpoint — + // mounting both at the same time would put two affordances in front + // of the user. return ( - <> - {/* Sticky FAB on `