From b1f71912a29c84ac062bf572d137c22257bbe76e Mon Sep 17 00:00:00 2001 From: Mattsson <111893710+mattssonn@users.noreply.github.com> Date: Wed, 5 Aug 2026 14:19:36 +0200 Subject: [PATCH] feat(analytics): show static nav chrome in session replays (#1412) * feat(analytics): show static nav chrome in session replays Replays previously masked every text node via maskTextSelector '*', which made them unreadable: even sidebar labels and buttons were asterisks. Add a fail-safe maskTextFn: text stays masked unless its nearest tagged ancestor is data-ph-unmask, and data-ph-mask re-masks user data nested inside an unmasked container. Untagged text stays masked, so a forgotten tag can never leak user data. Tags the four nav containers and the skip link; the notification count bubbles inside them are re-masked. User menu, company switcher and page titles stay masked on purpose: on detail pages the title is user data. Co-Authored-By: Claude Fable 5 * docs(analytics): use English domain terms in masking comment CodeRabbit: comment used 'enskild firma' and 'personnummer'; repo guideline is English for all comments. Co-Authored-By: Claude Fable 5 --------- Co-authored-by: Claude Fable 5 --- app/(dashboard)/layout.tsx | 1 + components/dashboard/DashboardNav.tsx | 22 +++++++++++++------- instrumentation-client.ts | 30 +++++++++++++++++++++------ 3 files changed, 39 insertions(+), 14 deletions(-) diff --git a/app/(dashboard)/layout.tsx b/app/(dashboard)/layout.tsx index 990004e4..0c4139e7 100644 --- a/app/(dashboard)/layout.tsx +++ b/app/(dashboard)/layout.tsx @@ -303,6 +303,7 @@ export default async function DashboardLayout({ > {/* Skip to content link for keyboard/screen reader users */} diff --git a/components/dashboard/DashboardNav.tsx b/components/dashboard/DashboardNav.tsx index 92b7c631..3bd34c3e 100644 --- a/components/dashboard/DashboardNav.tsx +++ b/components/dashboard/DashboardNav.tsx @@ -548,7 +548,7 @@ export default function DashboardNav({ companyName: _companyName, entityType, pa : null const countBubble = (badge: number) => ( - + {badge > 99 ? '99+' : badge} ) @@ -651,7 +651,7 @@ export default function DashboardNav({ companyName: _companyName, entityType, pa cn('h-[17px] w-[17px]', active ? 'text-primary' : 'text-muted-foreground group-hover:text-foreground'), )} {badge !== null && ( - + {badge > 99 ? '99' : badge} )} @@ -727,7 +727,10 @@ export default function DashboardNav({ companyName: _companyName, entityType, pa the aside width animates: no DOM swap, one continuous motion. The inactive layer is absolute (no layout), faded, nudged sideways and inert. */} + {/* data-ph-unmask: nav labels are static i18n chrome; count + bubbles inside carry data-ph-mask (nearest tag wins). */}