basalt-ui 1.17.0 → 1.19.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (112) hide show
  1. package/AGENTS.md +1 -1
  2. package/CHANGELOG.md +14 -0
  3. package/agent/rules/basalt-charts.md +12 -0
  4. package/agent/rules/basalt-mantine.md +71 -6
  5. package/agent/rules/basalt-router.md +129 -30
  6. package/agent/rules/basalt-tokens.md +2 -1
  7. package/agent/skills/basalt-design/SKILL.md +2 -2
  8. package/dist/charts/kinds/DualPanel.d.ts +7 -0
  9. package/dist/charts/kinds/DualPanel.d.ts.map +1 -1
  10. package/dist/charts/kinds/DualPanel.js +10 -3
  11. package/dist/charts/primitives/CartesianChart.d.ts +17 -0
  12. package/dist/charts/primitives/CartesianChart.d.ts.map +1 -1
  13. package/dist/charts/primitives/CartesianChart.js +5 -2
  14. package/dist/charts/primitives/ChartTooltip.d.ts +6 -1
  15. package/dist/charts/primitives/ChartTooltip.d.ts.map +1 -1
  16. package/dist/charts/primitives/ChartTooltip.js +2 -1
  17. package/dist/charts/series.d.ts +5 -0
  18. package/dist/charts/series.d.ts.map +1 -1
  19. package/dist/index.d.ts +1 -1
  20. package/dist/index.d.ts.map +1 -1
  21. package/dist/index.js +8 -0
  22. package/dist/nav/types.d.ts +110 -0
  23. package/dist/nav/types.d.ts.map +1 -0
  24. package/dist/nav/types.js +0 -0
  25. package/dist/router-tanstack/index.d.ts +9 -2
  26. package/dist/router-tanstack/index.d.ts.map +1 -1
  27. package/dist/router-tanstack/index.js +7 -0
  28. package/dist/router-tanstack/nav.d.ts +163 -0
  29. package/dist/router-tanstack/nav.d.ts.map +1 -0
  30. package/dist/router-tanstack/nav.js +38 -0
  31. package/dist/router-tanstack/nav.test.d.ts +2 -0
  32. package/dist/router-tanstack/nav.test.d.ts.map +1 -0
  33. package/dist/router-tanstack/search-param-store.d.ts +19 -9
  34. package/dist/router-tanstack/search-param-store.d.ts.map +1 -1
  35. package/dist/router-tanstack/use-nav.d.ts +30 -0
  36. package/dist/router-tanstack/use-nav.d.ts.map +1 -0
  37. package/dist/router-tanstack/use-nav.js +71 -0
  38. package/dist/shell/app-breadcrumbs.d.ts +7 -9
  39. package/dist/shell/app-breadcrumbs.d.ts.map +1 -1
  40. package/dist/shell/app-breadcrumbs.js +8 -4
  41. package/dist/shell/app-mobile-nav.d.ts +23 -24
  42. package/dist/shell/app-mobile-nav.d.ts.map +1 -1
  43. package/dist/shell/app-mobile-nav.js +290 -68
  44. package/dist/shell/app-mobile-nav.module.css +166 -11
  45. package/dist/shell/app-mobile-nav.module.css.d.ts +11 -2
  46. package/dist/shell/app-mobile-nav.test.d.ts +2 -0
  47. package/dist/shell/app-mobile-nav.test.d.ts.map +1 -0
  48. package/dist/shell/app-sidebar.d.ts +3 -28
  49. package/dist/shell/app-sidebar.d.ts.map +1 -1
  50. package/dist/shell/app-sidebar.js +40 -174
  51. package/dist/shell/app-sidebar.module.css +3 -2
  52. package/dist/shell/app-sidebar.module.css.d.ts +0 -2
  53. package/dist/shell/index.d.ts +22 -65
  54. package/dist/shell/index.d.ts.map +1 -1
  55. package/dist/shell/index.js +38 -76
  56. package/dist/shell/mobile-nav-model.d.ts +41 -0
  57. package/dist/shell/mobile-nav-model.d.ts.map +1 -0
  58. package/dist/shell/mobile-nav-model.js +244 -0
  59. package/dist/shell/mobile-nav-model.test.d.ts +2 -0
  60. package/dist/shell/mobile-nav-model.test.d.ts.map +1 -0
  61. package/dist/shell/nav-count-badge.d.ts +11 -0
  62. package/dist/shell/nav-count-badge.d.ts.map +1 -0
  63. package/dist/shell/nav-count-badge.js +29 -0
  64. package/dist/surfaces.d.ts +1 -1
  65. package/dist/surfaces.js +1 -1
  66. package/dist/theme/index.d.ts.map +1 -1
  67. package/dist/theme/index.js +8 -7
  68. package/dist/theme/nav-link.module.css +4 -2
  69. package/dist/tokens/index.d.ts.map +1 -1
  70. package/dist/tokens/index.js +2 -0
  71. package/dist/tokens/palette.d.ts +15 -0
  72. package/dist/tokens/palette.d.ts.map +1 -1
  73. package/dist/tokens/palette.js +13 -0
  74. package/dist/tokens.css +2 -0
  75. package/llms.txt +3 -3
  76. package/package.json +1 -1
  77. package/src/charts/kinds/DualPanel.test.tsx +81 -1
  78. package/src/charts/kinds/DualPanel.tsx +26 -4
  79. package/src/charts/primitives/CartesianChart.test.tsx +173 -1
  80. package/src/charts/primitives/CartesianChart.tsx +29 -5
  81. package/src/charts/primitives/ChartTooltip.test.tsx +22 -1
  82. package/src/charts/primitives/ChartTooltip.tsx +7 -1
  83. package/src/charts/series.ts +5 -0
  84. package/src/index.ts +15 -5
  85. package/src/nav/types.ts +115 -0
  86. package/src/router-tanstack/index.ts +21 -2
  87. package/src/router-tanstack/nav.test.ts +111 -0
  88. package/src/router-tanstack/nav.ts +205 -0
  89. package/src/router-tanstack/search-param-store.ts +19 -9
  90. package/src/router-tanstack/use-nav.ts +146 -0
  91. package/src/shell/app-breadcrumbs.tsx +21 -19
  92. package/src/shell/app-mobile-nav.module.css +166 -11
  93. package/src/shell/app-mobile-nav.module.css.d.ts +11 -2
  94. package/src/shell/app-mobile-nav.test.tsx +369 -0
  95. package/src/shell/app-mobile-nav.tsx +436 -102
  96. package/src/shell/app-sidebar.module.css +3 -2
  97. package/src/shell/app-sidebar.module.css.d.ts +0 -2
  98. package/src/shell/app-sidebar.test.tsx +6 -10
  99. package/src/shell/app-sidebar.tsx +49 -206
  100. package/src/shell/index.test.tsx +62 -7
  101. package/src/shell/index.tsx +105 -164
  102. package/src/shell/mobile-nav-model.test.ts +296 -0
  103. package/src/shell/mobile-nav-model.ts +389 -0
  104. package/src/shell/nav-count-badge.tsx +39 -0
  105. package/src/surfaces.ts +1 -1
  106. package/src/theme/density-relations.test.ts +19 -1
  107. package/src/theme/index.ts +8 -7
  108. package/src/theme/nav-link.module.css +4 -2
  109. package/src/theme/spacing.test.ts +11 -0
  110. package/src/tokens/density.test.ts +2 -0
  111. package/src/tokens/index.ts +8 -5
  112. package/src/tokens/palette.ts +16 -0
package/AGENTS.md CHANGED
@@ -15,7 +15,7 @@ Toolchain: Bun runtime, oxlint + oxfmt, conventional commits (empty scope), `mas
15
15
  | `basalt-ui/vite` | mantine-coupled | basaltViteConfig(opts) — Vite preset for basalt-ui consumer apps; basaltAppPlugin(opts) — PWA head, manifest, and icon metadata derived from the token palette |
16
16
  | `basalt-ui/guard` | headless | checkSource, GUARD_RULES, Finding types — the headless theme-guard core |
17
17
  | `basalt-ui/query` | headless | createBasaltQueryClient, transport-agnostic unwrap, lazy BasaltQueryDevtools |
18
- | `basalt-ui/router-tanstack` | headless | TanStack Router bridge: useBasaltNav (active route) + useRouterBreadcrumbs + createSearchParamStore (single-select URL-state store) + createMultiSearchParamStore (multi-select URL-state store) |
18
+ | `basalt-ui/router-tanstack` | headless | TanStack Router bridge: defineNav/navGroup/navTarget (one typed nav definition) + useNav (sections + mobileNav, spread onto BasaltShell) + useBasaltNav (active route) + useRouterBreadcrumbs + createSearchParamStore (single-select URL-state store) + createMultiSearchParamStore (multi-select URL-state store) |
19
19
  | `basalt-ui/forms` | mantine-coupled | Mantine form adapter: useBasaltForm, field, FormErrorSummary, useFormDraft (Standard Schema) |
20
20
  | `basalt-ui/notifications` | mantine-coupled | Mantine notifications: notify helpers, typed registry, persisted history, NotificationBell, NotificationCenter |
21
21
  | `basalt-ui/commands` | mantine-coupled | typed command bus + overlay controller, toSpotlightActions, ShortcutsHelp, BasaltOverlays |
package/CHANGELOG.md CHANGED
@@ -1,3 +1,17 @@
1
+ # [1.19.0](https://github.com/jkrumm/basalt-ui/compare/v1.18.0...v1.19.0) (2026-08-20)
2
+
3
+
4
+ ### Features
5
+
6
+ * make the mobile bar navigate, driven by one typed nav definition ([1ebba92](https://github.com/jkrumm/basalt-ui/commit/1ebba9267267ff26e988c876f17839b05d61689d))
7
+
8
+ # [1.18.0](https://github.com/jkrumm/basalt-ui/compare/v1.17.0...v1.18.0) (2026-08-19)
9
+
10
+
11
+ ### Features
12
+
13
+ * render tooltips on cursor followers, and gate managed-doc drift ([6b46613](https://github.com/jkrumm/basalt-ui/commit/6b466136e1b1dbd88934d339420179768b3e9f8b)), closes [#52](https://github.com/jkrumm/basalt-ui/issues/52)
14
+
1
15
  # [1.17.0](https://github.com/jkrumm/basalt-ui/compare/v1.16.0...v1.17.0) (2026-08-18)
2
16
 
3
17
 
@@ -145,6 +145,18 @@ ships no map kind.
145
145
  onto one domain value, silently dropping one from the plot (see #6 below for why that's the
146
146
  cursor's problem too). `Heatmap` is deliberately excluded: its existing `colLabel`/`rowLabel`
147
147
  already are that seam, and a second prop over one concern would fork them.
148
+ **The x axis is CATEGORICAL, and this is not inferable from the API.** `CartesianChart` builds
149
+ its x scale as `scalePoint<string>`, so N points are N evenly spaced positions whatever the
150
+ values behind the keys — there is no linear or time x scale. Correct and invisible for a domain
151
+ that is already a regular grid (five-minute buckets ARE evenly spaced); wrong for an event-shaped
152
+ series (speed-test runs, deploys, sessions). Two silent consequences: on a page sharing one
153
+ cursor, event points draw at equal spacing, so the crosshair marks the right point at a different
154
+ screen x than a regularly-sampled sibling — the correlation stays legible in the numbers and not
155
+ in the geometry; and because the domain value must be unique, two events at the same instant
156
+ collapse onto one position and one stops being drawn. `getX` returning a date string reads like a
157
+ time axis and is not one. Tracked as issue #52; a chart whose x is a measured quantity currently
158
+ needs a `basalt/hand-rolled-plot` exemption.
159
+
148
160
  6. **The cursor is shared by default.** No provider needed — `useChartCursor` reads a module-level
149
161
  external store (`useSyncExternalStore`), so every `CartesianChart`/`ChartFrame`-composed chart on
150
162
  the page shares one cursor out of the box. `ChartCursorScope` **isolates** a subtree onto a
@@ -60,8 +60,9 @@ Restraint is the #1 lever — neutrals carry the surface, the accent only points
60
60
  reads as a button sitting in the nav rather than as the current location. Inactive = muted text,
61
61
  faint icon, hover ink-6%; an active row hovers one step further to ink-13%. This is baked into
62
62
  the theme's NavLink defaults (`--nl-*` vars) and holds for _every_ render path, including a
63
- consumer's router `<Link>` passed via `renderNavLink`. Child items indent with a 1px `divider`
64
- left border; active child = accent text, weight 600.
63
+ consumer's router `<Link>` hosted through `SidebarItem.Anchor`. Child items indent with a 1px
64
+ `divider` left border; active child = accent text, weight 600. The MOBILE bar is the one
65
+ deliberate exception to the accent-icon half of this rule — see "Mobile bar" below.
65
66
  - **Buttons**: the primary action = filled accent (`variant="filled"`), **exactly one per view**.
66
67
  Every other/secondary action = `variant="default"` (neutral). Do **not** use a colored
67
68
  `variant="light"` for routine actions.
@@ -280,8 +281,8 @@ shadow-surfaces.test.ts` mechanically enumerates every site that applies either
280
281
  `BasaltShell` is the canonical single-mount. When a fully custom layout is needed, the shell
281
282
  sub-components are available individually from `basalt-ui`:
282
283
 
283
- - `AppSidebar` — desktop sidebar (sections, collapse, brand, footer/settings extras)
284
- - `MobileNav` — bottom-tab mobile nav
284
+ - `AppSidebar` — desktop sidebar (sections, collapse, brand, settings/account extras)
285
+ - `MobileNav` — the bottom tab bar (takes a `MobileNavModel` from `projectMobileNav`)
285
286
  - `AppBreadcrumbs` — breadcrumb bar (reads from `useRouterBreadcrumbs` or a manual trail)
286
287
  - `PageHeaderProvider` + `PageActions` + `PageActionsOutlet` — portal-based page action slots
287
288
  - `NavCountBadge` — count badge for sidebar nav items
@@ -294,13 +295,14 @@ import {
294
295
  PageHeaderProvider,
295
296
  PageActionsOutlet,
296
297
  NavCountBadge,
298
+ projectMobileNav,
297
299
  } from 'basalt-ui'
298
300
 
299
301
  function CustomShell({ children }: { children: React.ReactNode }) {
300
302
  return (
301
303
  <PageHeaderProvider>
302
- <AppSidebar sections={sections} brand={brand} renderNavLink={renderNavLink} />
303
- <MobileNav sections={mobileSections} renderNavLink={renderNavLink} />
304
+ <AppSidebar sections={sections} brand={brand} />
305
+ <MobileNav model={projectMobileNav(sections)} />
304
306
  <main>
305
307
  <AppBreadcrumbs />
306
308
  <PageActionsOutlet />
@@ -314,6 +316,69 @@ function CustomShell({ children }: { children: React.ReactNode }) {
314
316
  Prefer `BasaltShell` unless the layout genuinely diverges — it already composes all of the above
315
317
  with the correct wiring (collapse persistence, mobile breakpoint, breadcrumb integration).
316
318
 
319
+ ## Mobile bar
320
+
321
+ The bottom bar is a **tab bar, not a menu**: a slot is a DESTINATION, so a tap navigates instantly
322
+ with nothing to dismiss. There is no full-height mobile sidebar drawer — everything it used to hold
323
+ (nav, account, settings, theme switcher) is reachable from the trailing More slot instead.
324
+
325
+ `BasaltShell` builds the whole thing from the same `SidebarSection[]` the desktop sidebar renders:
326
+ **`projectMobileNav`** projects those sections onto the bar and **`MobileNav`** paints the result.
327
+ There is no second nav model to maintain, and in the common case no configuration at all.
328
+
329
+ **Surface inference — the part you do not configure.** For any slot holding `n` rows:
330
+
331
+ | `n` | surface |
332
+ | ----------------------- | ------------------------------------------------------------------------- |
333
+ | 0 | the slot is dropped |
334
+ | 1 | a plain navigation link — a group of one IS a destination |
335
+ | ≤ `menuMax` (default 6) | a Mantine `Menu` that pops upward out of the tab, content-sized, no scrim |
336
+ | > `menuMax` | a bottom `Drawer` sheet with a grabber and an internal `ScrollArea` |
337
+
338
+ Six is arithmetic, not taste: 6 × 44px + 8px padding = 272px against 415px of headroom on the
339
+ smallest supported viewport, which is why the menu is configured `flip: false` and can never render
340
+ below the fold. Move a destination in or out of the More slot and the surface changes itself.
341
+
342
+ **Placement** is one field per destination — `SidebarItem`'s `mobile`: `'tab'` gives it a slot of
343
+ its own, `'more'` (the default) makes it an overflow row, `'hidden'` drops it from mobile entirely.
344
+ A whole section claims one slot with `mobile: { tab: true }` (its destinations then open per the
345
+ table above) or leaves mobile with `mobile: false`. Configure nothing and the first four non-disabled
346
+ top-level destinations take slots — a nav that works before anyone has thought about mobile.
347
+
348
+ **`MobileNavConfig`** (the `mobileNav` prop) is the escape hatch, not the interface: `tabs` for an
349
+ explicit slot order by key when the bar order must differ from the sidebar's, `maxTabs` (5),
350
+ `menuMax` (6), `moreLabel`, `moreExtra`, and `getScrollElement` for re-tap-to-top when the page
351
+ scrolls inside a container rather than the document.
352
+
353
+ Design rules specific to the bar, each one load-bearing:
354
+
355
+ - **The active indicator is a neutral pill behind the ICON only** — `--vx-ink` at 10% plus
356
+ `--vx-radius-ctrl`, the Material-3 idiom. Never the identity blue, and never a full-tab fill: a
357
+ filled tab reads as chrome instead of as a tab bar. This is the exception to "active nav = accent
358
+ icon" in the palette rules above; the desktop sidebar keeps that rule unchanged.
359
+ - **A count renders as an 8px accent dot** on the slot icon, not a number — a count glyph is
360
+ unreadable at 56px. `SidebarItem.count` drives it; `SidebarItem.badge` (a rich node) stays
361
+ desktop-only.
362
+ - **Press feedback is the icon, not the hit box**: `.tab:active` scales the icon to 0.9 over 140ms.
363
+ `touch-action: manipulation` + `-webkit-tap-highlight-color: transparent` kill the 300ms
364
+ double-tap delay and the blue flash.
365
+ - **Every `:hover` rule sits behind `@media (hover: hover)`.** Ungated, a tapped row stays lit until
366
+ something else is tapped.
367
+ - **Re-tapping the ACTIVE slot scrolls to top** instead of pushing a redundant history entry.
368
+ - **Never add `padding-bottom: env(safe-area-inset-bottom)` to the bar.** Mantine's own
369
+ `AppShell.Footer` rule already grows the footer box by the inset and pads its content; adding it
370
+ again double-counts. The one real gap is `AppShell.Main`, whose padding is computed from an offset
371
+ var that excludes the inset — the shell closes that itself.
372
+ - **Reduced motion** is read explicitly via `useReducedMotion` (Mantine's
373
+ `respectReducedMotion` defaults to `false` and basalt does not change it) and zeroes both the menu
374
+ and sheet transitions.
375
+
376
+ Accessibility, all handled by the shell: the bar is `<nav aria-label="Primary">`, the active slot
377
+ carries `aria-current="page"`, every slot carries `aria-label` (the visible text is the short
378
+ label), menus get `menuItemTabIndex={0}` for the WAI-ARIA disclosure pattern, and the row height
379
+ floors at 44px with the bar at 48px regardless of the `density` level. **Do not hand-set
380
+ `aria-haspopup` / `aria-expanded`** on a `Menu.Target` child — Mantine clones them on.
381
+
317
382
  ## Icons
318
383
 
319
384
  basalt-ui takes icons as `ReactNode` (no icon dependency shipped). Use a single icon library across the
@@ -1,6 +1,6 @@
1
1
  ---
2
2
  source: basalt-ui
3
- description: TanStack Router conventions for basalt-ui apps — file-based routes, typed search params, and loader/query coupling. Advisory (ships an optional ./router-tanstack adapter for active-state and breadcrumb wiring).
3
+ description: TanStack Router conventions for basalt-ui apps — file-based routes, typed search params, loader/query coupling, and the one typed nav definition that drives both the desktop sidebar and the mobile bar. Advisory (ships an optional ./router-tanstack adapter).
4
4
  paths:
5
5
  - 'src/routes/**'
6
6
  - 'apps/**/src/routes/**'
@@ -8,13 +8,14 @@ paths:
8
8
 
9
9
  # Basalt Router — TanStack Router Conventions
10
10
 
11
- basalt-ui is **router-agnostic** — `BasaltShell` takes a `NavLinkRenderer` for nav rendering and
12
- receives active state from the consumer. Nav active-state can now be resolved via
13
- `useBasaltNav().isActive(item.href)` from `basalt-ui/router-tanstack`, and breadcrumbs via
14
- `useRouterBreadcrumbs()`; navigation itself stays with TanStack's typed `<Link>` / `useNavigate`.
15
- Badge counts and navigation wiring remain consumer-side. This rule is the recommended opinion layer
16
- when you use TanStack Router (the basalt-ui default). It is **advisory** — adopt it for
17
- consistency; nothing in the framework enforces it.
11
+ basalt-ui is **router-agnostic** — the shell renders every pixel of nav chrome and hosts the
12
+ consumer's router component through one seam, `SidebarItem.Anchor` (a **`NavAnchor`**). With
13
+ TanStack Router you never write that seam by hand: **`defineNav`** declares the whole navigation
14
+ once and **`useNav`** resolves it against the live router, returning the two props `BasaltShell`
15
+ needs. Breadcrumbs come from **`useRouterBreadcrumbs`**, ad-hoc active checks from
16
+ **`useBasaltNav`**. This rule is the recommended opinion layer when you use TanStack Router (the
17
+ basalt-ui default). It is **advisory** — adopt it for consistency; nothing in the framework
18
+ enforces it.
18
19
 
19
20
  ## Adding a page
20
21
 
@@ -23,7 +24,7 @@ consistency; nothing in the framework enforces it.
23
24
  3. Add `validateSearch` with a Zod schema parsed via a function — not the schema passed directly.
24
25
  4. Define `loaderDeps` to forward search params to the loader (required for search-param-driven queries).
25
26
  5. Add a `loader` that calls `ensureQueryData` for each query the page needs.
26
- 6. Register the page in your `BasaltShell` sidebar sections (`SidebarSection[]`).
27
+ 6. Register the page in your `defineNav` definition (see "One typed nav definition" below).
27
28
 
28
29
  ## Route structure
29
30
 
@@ -146,28 +147,126 @@ function AppBreadcrumbs() {
146
147
  }
147
148
  ```
148
149
 
149
- **renderNavLink pattern** — wire the shell's `NavLinkRenderer` with typed TanStack routes.
150
- basalt does NOT ship this renderer; typed-route `to` is the consumer's typed concern:
150
+ **One typed nav definition** — `defineNav` / `navGroup` / `navTarget` / `useNav`.
151
+
152
+ The whole navigation — desktop sidebar and mobile bar — is declared once in a **leaf** module
153
+ (`src/lib/nav.tsx`), which imports `@tanstack/react-router` and `basalt-ui/router-tanstack` and
154
+ **never** `routeTree.gen` or `__root.tsx`, so command palettes and redirects can import it without
155
+ closing a cycle.
156
+
157
+ ```tsx
158
+ import { linkOptions } from '@tanstack/react-router'
159
+ import { defineNav, navGroup } from 'basalt-ui/router-tanstack'
160
+
161
+ const ICON = 18
162
+
163
+ export const NAV = defineNav({
164
+ groups: [
165
+ navGroup({ id: 'health', label: 'Health', icon: <IconHeartbeat size={ICON} /> }, [
166
+ {
167
+ id: 'garmin',
168
+ label: 'Garmin Health',
169
+ short: 'Garmin', // bar/menu label — keep it ≤ 10 chars
170
+ mobile: 'tab', // give this destination its own slot on the mobile bar
171
+ icon: <IconHeartbeat size={ICON} />,
172
+ link: linkOptions({ to: '/garmin-health', search: { window: '30d' } }),
173
+ },
174
+ {
175
+ id: 'reading',
176
+ label: 'Reading',
177
+ icon: <IconBook size={ICON} />,
178
+ link: linkOptions({ to: '/reading' }),
179
+ children: [
180
+ {
181
+ id: 'reading-stats',
182
+ label: 'Stats',
183
+ icon: <IconChartBar size={ICON} />,
184
+ link: linkOptions({ to: '/reading/stats' }),
185
+ },
186
+ ],
187
+ },
188
+ ]),
189
+ ],
190
+ })
191
+ ```
151
192
 
152
- ```ts
153
- import { Link } from '@tanstack/react-router'
154
- import { NavLink as MantineNavLink } from '@mantine/core'
155
- import { useBasaltNav } from 'basalt-ui/router-tanstack'
156
- import type { NavLinkRenderer } from 'basalt-ui'
157
-
158
- const { isActive } = useBasaltNav()
159
-
160
- const renderNavLink: NavLinkRenderer = (item, _flags) => (
161
- <MantineNavLink
162
- component={Link}
163
- to={item.href ?? '/'}
164
- label={item.label}
165
- leftSection={item.icon}
166
- rightSection={item.badge}
167
- active={isActive(item.href ?? '/')}
168
- />
193
+ Rules that are not stylistic:
194
+
195
+ - **Route options live under `link:`, wrapped in TanStack's own `linkOptions`** — never spread flat
196
+ onto the item. `to` / `search` / `params` get the router's full validation there, while `id`,
197
+ `label`, `short`, `icon`, `mobile`, `disabled` and `exact` ride outside it. A flat shape
198
+ (`{ id, label, to }`) validates `to` by ASSIGNABILITY, which does no excess-property checking, so
199
+ a typo'd metadata key would compile silently. A top-level `to:` is a compile error by design.
200
+ - **`search` may be a thunk** — `search: () => ({ date: format(new Date(), 'yyyy-MM-dd') })`
201
+ compiles without `as const` and re-evaluates at click time, so "today" never goes stale in a
202
+ long-lived tab.
203
+ - **Nesting is `children`**, and nested destinations are indexed by id just like top-level ones.
204
+ - **`NavItemMeta`**'s `mobile` field places the destination on the bar: `'tab'` (own slot),
205
+ `'more'` (the default — reachable through the More overlay) or `'hidden'`. A group claims a slot
206
+ of its own with `mobile: { tab: true }`, and leaves mobile entirely with `mobile: false`.
207
+
208
+ ```tsx
209
+ // __root.tsx — four lines replace a hand-written sections array, two render callbacks,
210
+ // a NAV_TARGETS table and one useMatchRoute call per destination.
211
+ import { useNav } from 'basalt-ui/router-tanstack'
212
+ import { NAV } from '../lib/nav'
213
+
214
+ const nav = useNav(NAV, { badges: { calendar: unread } }) // keys autocomplete; a typo is an error
215
+ return (
216
+ <BasaltShell brand={brand} {...nav}>
217
+ {children}
218
+ </BasaltShell>
169
219
  )
170
220
  ```
171
221
 
172
- Navigation (`.navigate()`, `<Link to={…}>`) is always the consumer's typed concern — the adapter
173
- exposes no `navigate()` helper so that TanStack's route-tree types remain authoritative.
222
+ **`useNav`** returns `{ sections, mobileNav }` — both are `BasaltShell` props, so spread it. It
223
+ resolves `active` per destination through `useMatchRoute` (prefix match by default, exact when the
224
+ item sets `exact: true`, and always exact for `'/'`), and builds each destination's **`NavAnchor`**
225
+ so `preload="intent"`, middle-click and back/forward keep working. `badges` values that are
226
+ `number` become `SidebarItem.count` (a desktop **`NavCountBadge`** plus an accent dot on the mobile
227
+ slot); anything else becomes `SidebarItem.badge`, which is desktop-only because a count glyph is
228
+ unreadable in a 56px tab.
229
+
230
+ **`navTarget`** returns one destination's link options, typed per id — spread it into `<Link>`,
231
+ `router.navigate()` or `redirect()` instead of restating the route and its default search:
232
+
233
+ ```ts
234
+ throw redirect(navTarget(NAV, 'garmin')) // exactly { to: '/garmin-health', search: { window: '30d' } }
235
+ ```
236
+
237
+ **`flattenNav`** walks the definition depth-first (parent, then children), tagging each destination
238
+ with `groupId` / `groupLabel` — the leaf every other surface reads, e.g. projecting the whole nav
239
+ into Spotlight commands in one `.map()`.
240
+
241
+ ### The prerequisite that makes all of this real
242
+
243
+ **Without the `Register` module augmentation, `defineNav` validates NOTHING and reports zero
244
+ errors.** `RegisteredRouter` falls back to `AnyRouter` when the consumer has not written:
245
+
246
+ ```ts
247
+ declare module '@tanstack/react-router' {
248
+ interface Register {
249
+ router: typeof router
250
+ }
251
+ }
252
+ ```
253
+
254
+ With that missing, every `to` widens to `string`, a nonexistent route path compiles, a missing
255
+ required `search` compiles, and the API looks like it is working while catching nothing. This is
256
+ the single highest-value thing to verify when adopting the config: change one `to` to a garbage
257
+ path and confirm you get a compile error listing the real route paths. If you don't, the
258
+ augmentation is missing or `tsr generate` has not run.
259
+
260
+ Second thing to know before you migrate: TypeScript cannot catch a destination that FORGETS a
261
+ default `search` when the route validates with `validateSearch: (raw: Record<string, unknown>) => T`
262
+ — that gives TanStack an input type where no key is required. Diff an old hand-written target table
263
+ against the new definition by hand, key by key.
264
+
265
+ Error messages here are the router's own and they are large: one wrong `to` prints the whole
266
+ route-path union plus a `RouterCore<…>` blob, because TanStack's `Constrain` falls back to the
267
+ whole constraint. That is not misuse — read the union for the intended path and fix the string.
268
+ **Do not reach for `as never` to make it quiet**; the checking is the entire point of the config.
269
+
270
+ Navigation itself (`.navigate()`, `<Link to={…}>`) stays the consumer's typed concern — the adapter
271
+ still exposes no `navigate()` helper, so TanStack's route-tree types remain authoritative. What
272
+ `navTarget` hands back is the ARGUMENT to those calls, never the call.
@@ -67,7 +67,8 @@ Retuning a fill OUTSIDE the band breaks its label. `theme/contrast.test.ts` fail
67
67
  or genuine multi-series separation. A count badge is a signal → it may carry blue; a nav active-state
68
68
  is UI chrome → it stays neutral (`NavCountBadge` already encodes this). Active nav renders a
69
69
  **neutral surface fill** + plain text + a weight bump, never the accent — baked into the theme's
70
- NavLink `--nl-*` defaults across every render path (including a router `<Link>` via `renderNavLink`).
70
+ NavLink `--nl-*` defaults across every render path (including a router `<Link>` hosted through
71
+ `SidebarItem.Anchor`).
71
72
  - **Accent restraint, mechanically.** The accent lands on exactly the single primary CTA per view
72
73
  (`variant="filled"`), focus rings, links, and small status pops — nowhere else. Routine/secondary
73
74
  buttons use `variant="default"` (neutral); don't reach for a colored `variant="light"` (it reads
@@ -227,7 +227,7 @@ Every chart composes the shipped primitives — never hand-rolled equivalents:
227
227
 
228
228
  - **`ChartCard`** wrapper (title + info-tooltip + extra slot, consistent margin).
229
229
  - **`ChartLegend`** (`line | bar | split | splitLine` shapes) — never hand-rolled legend markup.
230
- - **`ChartTooltip`** + `TooltipHeader` / `TooltipRow` / `TooltipBody` — never import `@visx/tooltip`
230
+ - **`ChartTooltipFloat`** + `TooltipHeader` / `TooltipRow` / `TooltipBody` — never import `@visx/tooltip`
231
231
  directly (oxlint bans it in chart files).
232
232
  - **`AxisBottomDate`** / **`AxisLeftNumeric`** / **`AxisRightNumeric`** — never raw
233
233
  `<AxisLeft>`/`<AxisBottom>` (they miss theme tokens + smart ticks). Now lint-enforced:
@@ -262,7 +262,7 @@ resolve/render with no `@mantine/*` installed — CI-tested, repo-local only, no
262
262
  - Every color comes from a token (`VX.*` / palette) — `basalt-ui check-theme` green, zero raw hex.
263
263
  - One hue per metric; categorical multi-color only where the data is genuinely categorical.
264
264
  - Neutral line/axis/grid; soft `AreaGradient` on plain metric lines, opaque stacked-area bands.
265
- - Tooltip = `ChartCard` / `ChartTooltip` primitives, one swatch per row — no hand-rolled markup.
265
+ - Tooltip = `ChartCard` / `ChartTooltipFloat` primitives, one swatch per row — no hand-rolled markup.
266
266
  - Looks right in BOTH schemes (toggled and checked: glow on dark, contrast on light).
267
267
  - New colors landed as `{ light, dark }` pairs in the palette, not inline.
268
268
  - Nothing reads `localStorage.getItem('theme')`.
@@ -69,6 +69,13 @@ export type DualPanelProps<T> = {
69
69
  * alongside the hovered datum.
70
70
  */
71
71
  formatHeader?: (key: string, d: T) => string;
72
+ /**
73
+ * Render this chart's tooltip when it is a cursor FOLLOWER, not only when it is the SOURCE —
74
+ * same seam and same anchored-positioning behaviour as `CartesianTooltipConfig.onFollow`. Default
75
+ * false (today's behaviour). Only the SOURCE's tooltip is `aria-live`; a follower's is visual
76
+ * only.
77
+ */
78
+ onFollow?: boolean;
72
79
  /** Legend config forwarded to `ChartFrame`; `false` disables the legend (sparkline escape).
73
80
  * Default `{ placement: 'bottom' }`. */
74
81
  legend?: ChartLegendConfig | false;
@@ -1 +1 @@
1
- {"version":3,"file":"DualPanel.d.ts","sourceRoot":"","sources":["../../../src/charts/kinds/DualPanel.tsx"],"names":[],"mappings":"AAmBA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,yBAAyB,CAAA;AACvD,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,mBAAmB,CAAA;AAIzD,OAAO,KAAK,EAAE,iBAAiB,EAAE,WAAW,EAAe,MAAM,WAAW,CAAA;AAK5E,MAAM,MAAM,cAAc,CAAC,CAAC,IAAI;IAC9B,IAAI,EAAE,CAAC,EAAE,CAAA;IACT,mFAAmF;IACnF,MAAM,CAAC,EAAE,MAAM,CAAA;IACf,OAAO,EAAE,MAAM,CAAA;IACf,IAAI,EAAE,CAAC,CAAC,EAAE,CAAC,KAAK,MAAM,CAAA;IACtB;gEAC4D;IAC5D,OAAO,CAAC,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,MAAM,CAAA;IACjC;;;;OAIG;IACH,gBAAgB,CAAC,EAAE,gBAAgB,CAAA;IACnC;8DAC0D;IAC1D,MAAM,EAAE,WAAW,CAAC,CAAC,CAAC,EAAE,CAAA;IACxB,+EAA+E;IAC/E,UAAU,CAAC,EAAE,CAAC,MAAM,EAAE,MAAM,CAAC,GAAG,MAAM,CAAA;IACtC,2EAA2E;IAC3E,WAAW,CAAC,EAAE;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,EAAE,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAA;KAAE,CAAA;IACxD,wGAAwG;IACxG,WAAW,CAAC,EAAE;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAC;QAAC,MAAM,CAAC,EAAE,OAAO,CAAA;KAAE,EAAE,CAAA;IAClE,oDAAoD;IACpD,QAAQ,CAAC,EAAE,QAAQ,EAAE,CAAA;IACrB,8DAA8D;IAC9D,MAAM,EAAE,CAAC,CAAC,EAAE,CAAC,KAAK,MAAM,GAAG,IAAI,CAAA;IAC/B,QAAQ,EAAE,MAAM,CAAA;IAChB,gBAAgB,EAAE,MAAM,CAAA;IACxB,gBAAgB,EAAE,MAAM,CAAA;IACxB,6DAA6D;IAC7D,WAAW,CAAC,EAAE,MAAM,CAAA;IACpB,SAAS,EAAE,CAAC,CAAC,EAAE,MAAM,KAAK,MAAM,CAAA;IAChC,+FAA+F;IAC/F,YAAY,EAAE,CAAC,CAAC,EAAE,MAAM,KAAK,MAAM,CAAA;IACnC;;yFAEqF;IACrF,SAAS,CAAC,EAAE,CAAC,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,CAAC,KAAK,MAAM,CAAA;IACvC;;;OAGG;IACH,aAAa,CAAC,EAAE,CAAC,MAAM,EAAE,MAAM,CAAC,GAAG,MAAM,CAAA;IACzC;iGAC6F;IAC7F,iBAAiB,CAAC,EAAE,MAAM,CAAA;IAC1B,kEAAkE;IAClE,YAAY,CAAC,EAAE,CAAC,CAAC,EAAE,CAAC,KAAK;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAA;KAAE,GAAG,IAAI,CAAA;IAC/D;;;;;OAKG;IACH,YAAY,CAAC,EAAE,CAAC,GAAG,EAAE,MAAM,EAAE,CAAC,EAAE,CAAC,KAAK,MAAM,CAAA;IAC5C;4CACwC;IACxC,MAAM,CAAC,EAAE,iBAAiB,GAAG,KAAK,CAAA;IAClC,+FAA+F;IAC/F,SAAS,CAAC,EAAE,MAAM,CAAA;IAClB,4FAA4F;IAC5F,SAAS,CAAC,EAAE,OAAO,CAAA;CACpB,CAAA;AAID;;;;;;;;;;;GAWG;AACH,iBAAS,cAAc,CAAC,CAAC,EAAE,KAAK,EAAE,cAAc,CAAC,CAAC,CAAC,+BAmClD;AAifD;;;GAGG;AACH,eAAO,MAAM,SAAS,EAA2B,OAAO,cAAc,CAAA"}
1
+ {"version":3,"file":"DualPanel.d.ts","sourceRoot":"","sources":["../../../src/charts/kinds/DualPanel.tsx"],"names":[],"mappings":"AAmBA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,yBAAyB,CAAA;AACvD,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,mBAAmB,CAAA;AAIzD,OAAO,KAAK,EAAE,iBAAiB,EAAE,WAAW,EAAe,MAAM,WAAW,CAAA;AAK5E,MAAM,MAAM,cAAc,CAAC,CAAC,IAAI;IAC9B,IAAI,EAAE,CAAC,EAAE,CAAA;IACT,mFAAmF;IACnF,MAAM,CAAC,EAAE,MAAM,CAAA;IACf,OAAO,EAAE,MAAM,CAAA;IACf,IAAI,EAAE,CAAC,CAAC,EAAE,CAAC,KAAK,MAAM,CAAA;IACtB;gEAC4D;IAC5D,OAAO,CAAC,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,MAAM,CAAA;IACjC;;;;OAIG;IACH,gBAAgB,CAAC,EAAE,gBAAgB,CAAA;IACnC;8DAC0D;IAC1D,MAAM,EAAE,WAAW,CAAC,CAAC,CAAC,EAAE,CAAA;IACxB,+EAA+E;IAC/E,UAAU,CAAC,EAAE,CAAC,MAAM,EAAE,MAAM,CAAC,GAAG,MAAM,CAAA;IACtC,2EAA2E;IAC3E,WAAW,CAAC,EAAE;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,EAAE,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAA;KAAE,CAAA;IACxD,wGAAwG;IACxG,WAAW,CAAC,EAAE;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAC;QAAC,MAAM,CAAC,EAAE,OAAO,CAAA;KAAE,EAAE,CAAA;IAClE,oDAAoD;IACpD,QAAQ,CAAC,EAAE,QAAQ,EAAE,CAAA;IACrB,8DAA8D;IAC9D,MAAM,EAAE,CAAC,CAAC,EAAE,CAAC,KAAK,MAAM,GAAG,IAAI,CAAA;IAC/B,QAAQ,EAAE,MAAM,CAAA;IAChB,gBAAgB,EAAE,MAAM,CAAA;IACxB,gBAAgB,EAAE,MAAM,CAAA;IACxB,6DAA6D;IAC7D,WAAW,CAAC,EAAE,MAAM,CAAA;IACpB,SAAS,EAAE,CAAC,CAAC,EAAE,MAAM,KAAK,MAAM,CAAA;IAChC,+FAA+F;IAC/F,YAAY,EAAE,CAAC,CAAC,EAAE,MAAM,KAAK,MAAM,CAAA;IACnC;;yFAEqF;IACrF,SAAS,CAAC,EAAE,CAAC,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,CAAC,KAAK,MAAM,CAAA;IACvC;;;OAGG;IACH,aAAa,CAAC,EAAE,CAAC,MAAM,EAAE,MAAM,CAAC,GAAG,MAAM,CAAA;IACzC;iGAC6F;IAC7F,iBAAiB,CAAC,EAAE,MAAM,CAAA;IAC1B,kEAAkE;IAClE,YAAY,CAAC,EAAE,CAAC,CAAC,EAAE,CAAC,KAAK;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAA;KAAE,GAAG,IAAI,CAAA;IAC/D;;;;;OAKG;IACH,YAAY,CAAC,EAAE,CAAC,GAAG,EAAE,MAAM,EAAE,CAAC,EAAE,CAAC,KAAK,MAAM,CAAA;IAC5C;;;;;OAKG;IACH,QAAQ,CAAC,EAAE,OAAO,CAAA;IAClB;4CACwC;IACxC,MAAM,CAAC,EAAE,iBAAiB,GAAG,KAAK,CAAA;IAClC,+FAA+F;IAC/F,SAAS,CAAC,EAAE,MAAM,CAAA;IAClB,4FAA4F;IAC5F,SAAS,CAAC,EAAE,OAAO,CAAA;CACpB,CAAA;AAID;;;;;;;;;;;GAWG;AACH,iBAAS,cAAc,CAAC,CAAC,EAAE,KAAK,EAAE,cAAc,CAAC,CAAC,CAAC,+BAmClD;AAggBD;;;GAGG;AACH,eAAO,MAAM,SAAS,EAA2B,OAAO,cAAc,CAAA"}
@@ -5,7 +5,7 @@ import { Group } from "@visx/group";
5
5
  import { scaleLinear, scalePoint } from "@visx/scale";
6
6
  import { Bar, LinePath } from "@visx/shape";
7
7
  import { Threshold } from "@visx/threshold";
8
- import { memo, useMemo } from "react";
8
+ import { memo, useMemo, useRef } from "react";
9
9
  import { AxisBottomDate, AxisLeftNumeric } from "../primitives/Axes.js";
10
10
  import {
11
11
  ChartTooltipFloat,
@@ -75,6 +75,7 @@ function DualPanelPlot(props) {
75
75
  bottomMaxAbsFloor,
76
76
  tooltipLabel,
77
77
  formatHeader,
78
+ onFollow,
78
79
  plot
79
80
  } = props;
80
81
  const { hidden } = plot;
@@ -259,11 +260,17 @@ function DualPanelPlot(props) {
259
260
  }),
260
261
  [visibleSeries, lineValid, xScale, topYScale, getX]
261
262
  );
263
+ const svgRef = useRef(null);
262
264
  const point = cursor.point;
263
265
  const sx = point !== null ? xScale(getX(point)) ?? 0 : 0;
264
266
  const syncedBar = point !== null ? getBar(point) : null;
267
+ const isFollowerRender = !cursor.isSource && onFollow === true;
268
+ const showTooltip = point !== null && (cursor.isSource || isFollowerRender);
269
+ const anchorX = isFollowerRender && point !== null ? sx : null;
270
+ const svgRect = anchorX === null ? void 0 : svgRef.current?.getBoundingClientRect();
271
+ const tooltipAnchor = svgRect === void 0 || anchorX === null ? cursor.anchor : { x: svgRect.left + margin.left + anchorX, y: svgRect.top + margin.top };
265
272
  return /* @__PURE__ */ jsxs(Fragment, { children: [
266
- /* @__PURE__ */ jsxs("svg", { width: plot.width, height: plot.height, children: [
273
+ /* @__PURE__ */ jsxs("svg", { ref: svgRef, width: plot.width, height: plot.height, children: [
267
274
  /* @__PURE__ */ jsxs(Group, { left: margin.left, top: margin.top, children: [
268
275
  /* @__PURE__ */ jsx(GridRows, { scale: topYScale, width: xMax, stroke: VX.grid, numTicks: 4 }),
269
276
  /* @__PURE__ */ jsx(ZoneRects, { zones: topZones, width: xMax, leftScale: topYScale }),
@@ -392,7 +399,7 @@ function DualPanelPlot(props) {
392
399
  ] }),
393
400
  point !== null && /* @__PURE__ */ jsx(Group, { left: margin.left, top: margin.top, children: /* @__PURE__ */ jsx(Crosshair, { x: sx, top: 0, bottom: innerH }) })
394
401
  ] }),
395
- cursor.isSource && point !== null && /* @__PURE__ */ jsxs(ChartTooltipFloat, { anchor: cursor.anchor, children: [
402
+ showTooltip && /* @__PURE__ */ jsxs(ChartTooltipFloat, { anchor: tooltipAnchor, ariaLive: cursor.isSource, children: [
396
403
  /* @__PURE__ */ jsx(
397
404
  TooltipHeader,
398
405
  {
@@ -77,6 +77,23 @@ export type CartesianTooltipConfig<T> = {
77
77
  prependRows?: (d: T, ctx: CartesianTooltipRowContext<T>) => ReactNode;
78
78
  /** Rows appended after the derived per-series rows. Same `ctx` as {@link prependRows}. */
79
79
  extraRows?: (d: T, ctx: CartesianTooltipRowContext<T>) => ReactNode;
80
+ /**
81
+ * Render this chart's tooltip when it is a cursor FOLLOWER (the crosshair came from a sibling
82
+ * chart sharing the cursor), not only when this chart is the SOURCE. Default false — today's
83
+ * behaviour, nothing moves. A follower has no pointer under it, so its tooltip is always ANCHORED
84
+ * to the crosshair — the same positioning `follow: false` gives the source — regardless of what
85
+ * `follow` is set to on this chart: there is no other valid position for a chart nobody is
86
+ * hovering. `follow` keeps governing the SOURCE's own tooltip exactly as before; the two options
87
+ * only interact for a follower, where `onFollow` always wins on positioning. Only the SOURCE's
88
+ * tooltip is `aria-live` — followers announce nothing, so a shared-cursor page doesn't fire N
89
+ * live-region updates per pointer move.
90
+ *
91
+ * Cost, since it scales with the page rather than the chart: anchoring reads one
92
+ * `getBoundingClientRect` per hovered frame (the same cost `follow: false` documents), so N
93
+ * opted-in followers pay N of them per frame. That is the reason this is opt-in per chart rather
94
+ * than a page-level switch — turn it on for the charts a reader actually correlates.
95
+ */
96
+ onFollow?: boolean;
80
97
  };
81
98
  /** Everything a mark renderer needs. Handed to `CartesianChart`'s child on every render. */
82
99
  export type PlotContext<T> = {
@@ -1 +1 @@
1
- {"version":3,"file":"CartesianChart.d.ts","sourceRoot":"","sources":["../../../src/charts/primitives/CartesianChart.tsx"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"CartesianChart.d.ts","sourceRoot":"","sources":["../../../src/charts/primitives/CartesianChart.tsx"],"names":[],"mappings":"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"}
@@ -228,7 +228,10 @@ function CartesianPlot({
228
228
  [visible, leftFormat, rightFormat]
229
229
  );
230
230
  const rows = tooltipEnabled && point !== null ? deriveTooltipRows(tooltipSeries, point, leftFormat) : [];
231
- const anchorX = tooltipCfg?.follow === false && point !== null && cursorX !== null ? cursorX : null;
231
+ const isFollowerRender = !cursor.isSource && tooltipCfg?.onFollow === true;
232
+ const showTooltip = tooltipEnabled && point !== null && (cursor.isSource || isFollowerRender);
233
+ const useAnchoredPosition = tooltipCfg?.follow === false || isFollowerRender;
234
+ const anchorX = useAnchoredPosition && point !== null && cursorX !== null ? cursorX : null;
232
235
  const svgRect = anchorX === null ? void 0 : svgRef.current?.getBoundingClientRect();
233
236
  const tooltipAnchor = svgRect === void 0 || anchorX === null ? cursor.anchor : { x: svgRect.left + margin.left + anchorX, y: svgRect.top + margin.top };
234
237
  const ctx = {
@@ -330,7 +333,7 @@ function CartesianPlot({
330
333
  }
331
334
  )
332
335
  ] }) }),
333
- tooltipEnabled && cursor.isSource && point !== null && /* @__PURE__ */ jsxs(ChartTooltipFloat, { anchor: tooltipAnchor, children: [
336
+ showTooltip && /* @__PURE__ */ jsxs(ChartTooltipFloat, { anchor: tooltipAnchor, ariaLive: cursor.isSource, children: [
334
337
  /* @__PURE__ */ jsx(
335
338
  TooltipHeader,
336
339
  {
@@ -32,11 +32,16 @@ export declare function TooltipBody({ children }: {
32
32
  * The box is measured after mount via `useLayoutEffect`, so the first paint of a given tooltip is
33
33
  * hidden rather than misplaced — a tooltip that flashes at the wrong corner reads as a bug.
34
34
  */
35
- export declare function ChartTooltipFloat({ anchor, children, }: {
35
+ export declare function ChartTooltipFloat({ anchor, ariaLive, children, }: {
36
36
  anchor: {
37
37
  x: number;
38
38
  y: number;
39
39
  } | null;
40
+ /** Whether this tooltip announces itself to screen readers on every position change. Default
41
+ * true. Pass false for a cursor FOLLOWER's tooltip — N followers on a shared-cursor page all
42
+ * announcing on every pointer move would make the page unusable with a screen reader; only the
43
+ * cursor SOURCE should be `aria-live`. */
44
+ ariaLive?: boolean;
40
45
  children: ReactNode;
41
46
  }): import("react").ReactPortal | null;
42
47
  //# sourceMappingURL=ChartTooltip.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"ChartTooltip.d.ts","sourceRoot":"","sources":["../../../src/charts/primitives/ChartTooltip.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAiB,SAAS,EAAE,MAAM,OAAO,CAAA;AAuBrD,uFAAuF;AACvF,wBAAgB,aAAa,CAAC,EAC5B,IAAI,EACJ,KAAK,EACL,UAAU,EACV,MAAuB,GACxB,EAAE;IACD,IAAI,EAAE,MAAM,CAAA;IACZ,KAAK,CAAC,EAAE,MAAM,CAAA;IACd,UAAU,CAAC,EAAE,MAAM,CAAA;IACnB;;;sGAGkG;IAClG,MAAM,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,MAAM,CAAA;CAClC,+BAoCA;AAED,4CAA4C;AAC5C,wBAAgB,UAAU,CAAC,EACzB,KAAK,EACL,KAAK,EACL,KAAK,EACL,UAAU,EACV,KAAK,EACL,WAAW,EACX,MAAM,GACP,EAAE;IACD,KAAK,EAAE,MAAM,CAAA;IACb,KAAK,EAAE,MAAM,CAAA;IACb,KAAK,EAAE,MAAM,CAAA;IACb,UAAU,CAAC,EAAE,MAAM,CAAA;IACnB,KAAK,CAAC,EAAE,KAAK,GAAG,MAAM,GAAG,KAAK,CAAA;IAC9B,WAAW,CAAC,EAAE,MAAM,CAAA;IACpB,uEAAuE;IACvE,MAAM,CAAC,EAAE,OAAO,CAAA;CACjB,+BA0CA;AAED,wBAAgB,WAAW,CAAC,EAAE,QAAQ,EAAE,EAAE;IAAE,QAAQ,EAAE,SAAS,CAAA;CAAE,+BAEhE;AAOD;;;;;;;GAOG;AACH,wBAAgB,iBAAiB,CAAC,EAChC,MAAM,EACN,QAAQ,GACT,EAAE;IACD,MAAM,EAAE;QAAE,CAAC,EAAE,MAAM,CAAC;QAAC,CAAC,EAAE,MAAM,CAAA;KAAE,GAAG,IAAI,CAAA;IACvC,QAAQ,EAAE,SAAS,CAAA;CACpB,sCA4CA"}
1
+ {"version":3,"file":"ChartTooltip.d.ts","sourceRoot":"","sources":["../../../src/charts/primitives/ChartTooltip.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAiB,SAAS,EAAE,MAAM,OAAO,CAAA;AAuBrD,uFAAuF;AACvF,wBAAgB,aAAa,CAAC,EAC5B,IAAI,EACJ,KAAK,EACL,UAAU,EACV,MAAuB,GACxB,EAAE;IACD,IAAI,EAAE,MAAM,CAAA;IACZ,KAAK,CAAC,EAAE,MAAM,CAAA;IACd,UAAU,CAAC,EAAE,MAAM,CAAA;IACnB;;;sGAGkG;IAClG,MAAM,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,MAAM,CAAA;CAClC,+BAoCA;AAED,4CAA4C;AAC5C,wBAAgB,UAAU,CAAC,EACzB,KAAK,EACL,KAAK,EACL,KAAK,EACL,UAAU,EACV,KAAK,EACL,WAAW,EACX,MAAM,GACP,EAAE;IACD,KAAK,EAAE,MAAM,CAAA;IACb,KAAK,EAAE,MAAM,CAAA;IACb,KAAK,EAAE,MAAM,CAAA;IACb,UAAU,CAAC,EAAE,MAAM,CAAA;IACnB,KAAK,CAAC,EAAE,KAAK,GAAG,MAAM,GAAG,KAAK,CAAA;IAC9B,WAAW,CAAC,EAAE,MAAM,CAAA;IACpB,uEAAuE;IACvE,MAAM,CAAC,EAAE,OAAO,CAAA;CACjB,+BA0CA;AAED,wBAAgB,WAAW,CAAC,EAAE,QAAQ,EAAE,EAAE;IAAE,QAAQ,EAAE,SAAS,CAAA;CAAE,+BAEhE;AAOD;;;;;;;GAOG;AACH,wBAAgB,iBAAiB,CAAC,EAChC,MAAM,EACN,QAAe,EACf,QAAQ,GACT,EAAE;IACD,MAAM,EAAE;QAAE,CAAC,EAAE,MAAM,CAAC;QAAC,CAAC,EAAE,MAAM,CAAA;KAAE,GAAG,IAAI,CAAA;IACvC;;;8CAG0C;IAC1C,QAAQ,CAAC,EAAE,OAAO,CAAA;IAClB,QAAQ,EAAE,SAAS,CAAA;CACpB,sCA4CA"}
@@ -121,6 +121,7 @@ const TOOLTIP_GAP = 12;
121
121
  const VIEWPORT_MARGIN = 8;
122
122
  function ChartTooltipFloat({
123
123
  anchor,
124
+ ariaLive = true,
124
125
  children
125
126
  }) {
126
127
  const ref = useRef(null);
@@ -151,7 +152,7 @@ function ChartTooltipFloat({
151
152
  {
152
153
  ref,
153
154
  role: "tooltip",
154
- "aria-live": "polite",
155
+ ...ariaLive && { "aria-live": "polite" },
155
156
  style: { ...TOOLTIP_STYLES, left, top, visibility: box === null ? "hidden" : "visible" },
156
157
  children
157
158
  }
@@ -58,6 +58,11 @@ export type SeriesStyle = {
58
58
  * that lies is the thing the derived-legend rule exists to prevent). The TOOLTIP ROW swatch and
59
59
  * the CROSSHAIR DOT deliberately do NOT honor it: those are 12px value-readout chips where a
60
60
  * sub-1 opacity reads as a rendering bug, not as data.
61
+ *
62
+ * Applies to STROKES only, so it silently no-ops on a `mark: 'bar'`/`'area'` series — a bar
63
+ * swatch has no stroke to dim. Dim those with `fillOpacity` instead. This bites the
64
+ * zone/reference-legend idiom in particular (`mark: 'bar'`, `getValue: () => null`), where
65
+ * reaching for `strokeOpacity` looks right and does nothing.
61
66
  */
62
67
  strokeOpacity?: number;
63
68
  /** Which y-axis this series is measured against. Default 'left'. `'right'` is what makes a
@@ -1 +1 @@
1
- {"version":3,"file":"series.d.ts","sourceRoot":"","sources":["../../src/charts/series.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"series.d.ts","sourceRoot":"","sources":["../../src/charts/series.ts"],"names":[],"mappings":"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"}