Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions .changeset/visual-tooltip-order.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
---
'@tanstack/charts': minor
---

Order grouped tooltip rows by rendered mark position by default: top-to-bottom
for x groups and left-to-right for y groups. Add `visual` as an explicit sort
policy while preserving color-domain, focus, and custom comparator ordering.
15 changes: 15 additions & 0 deletions API-FRICTION.md
Original file line number Diff line number Diff line change
Expand Up @@ -201,6 +201,7 @@ Each entry records:
| F-163 | Cross-row transforms lacked a public ownership boundary | API | resolved |
| F-164 | Sankey widths required a custom scene renderer | API | resolved |
| F-165 | Incidental D3 utilities leaked into core paths | API/Tooling | resolved |
| F-166 | Grouped tooltip order diverged from mark position | API | resolved |

## Findings

Expand Down Expand Up @@ -3986,3 +3987,17 @@ Each entry records:
every `d3-*` module plus `internmap` from compact consumers, while selected
transform, polar, geo, and curve features retain their owned D3
implementation.

### F-166 — Grouped tooltip order diverged from mark position

- Status: resolved
- Severity: medium
- Owner: API
- Observed in: reviewing the default grouped-tooltip row order
- Friction: the default followed the color domain, so a tooltip could list
series in an order unrelated to the marks under the pointer.
- Decision: default to visual order. X-grouped rows follow y position from top
to bottom; y-grouped rows follow x position from left to right. Preserve
`color-domain`, `focus`, and custom comparators as explicit policies.
- Verification: runtime tests cover both axes with input and color-domain order
opposed to the rendered mark order.
126 changes: 63 additions & 63 deletions benchmarks/comparison/bundle-baseline.json
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
{
"schemaVersion": 3,
"generatedAt": "2026-07-31T21:48:07.702Z",
"generatedAt": "2026-08-01T17:40:25.541Z",
"packageVersions": {
"tanstack": "0.3.0",
"tanstack": "0.3.1",
"chartjs": "4.5.1",
"echarts": "6.1.0",
"recharts": "3.10.1",
Expand All @@ -11,7 +11,7 @@
"sources": {
"tanstack": {
"kind": "workspace",
"revision": "c422a2ce45799d4edd63fdbde7ecb31daa3dae31"
"revision": "d563eba5d295dc3ddf60eecd2d8a9bd418aaf635"
},
"chartjs": {
"kind": "package",
Expand Down Expand Up @@ -44,88 +44,88 @@
},
"bundles": {
"tanstack-line-basic": {
"minifiedBytes": 72114,
"gzipBytes": 27318,
"brotliBytes": 24182,
"incrementalGzipBytes": 27318,
"incrementalBrotliBytes": 24182
"minifiedBytes": 72327,
"gzipBytes": 27384,
"brotliBytes": 24255,
"incrementalGzipBytes": 27384,
"incrementalBrotliBytes": 24255
},
"tanstack-line-interactive": {
"minifiedBytes": 77285,
"gzipBytes": 28970,
"brotliBytes": 25539,
"incrementalGzipBytes": 28970,
"incrementalBrotliBytes": 25539
"minifiedBytes": 77498,
"gzipBytes": 29036,
"brotliBytes": 25644,
"incrementalGzipBytes": 29036,
"incrementalBrotliBytes": 25644
},
"tanstack-line-advanced": {
"minifiedBytes": 84466,
"gzipBytes": 31307,
"brotliBytes": 27553,
"incrementalGzipBytes": 31307,
"incrementalBrotliBytes": 27553
"minifiedBytes": 84679,
"gzipBytes": 31379,
"brotliBytes": 27617,
"incrementalGzipBytes": 31379,
"incrementalBrotliBytes": 27617
},
"tanstack-bar-basic": {
"minifiedBytes": 79117,
"gzipBytes": 29968,
"brotliBytes": 26390,
"incrementalGzipBytes": 29968,
"incrementalBrotliBytes": 26390
"minifiedBytes": 79330,
"gzipBytes": 30040,
"brotliBytes": 26485,
"incrementalGzipBytes": 30040,
"incrementalBrotliBytes": 26485
},
"tanstack-bar-interactive": {
"minifiedBytes": 83147,
"gzipBytes": 31184,
"brotliBytes": 27466,
"incrementalGzipBytes": 31184,
"incrementalBrotliBytes": 27466
"minifiedBytes": 83360,
"gzipBytes": 31257,
"brotliBytes": 27474,
"incrementalGzipBytes": 31257,
"incrementalBrotliBytes": 27474
},
"tanstack-bar-advanced": {
"minifiedBytes": 83486,
"gzipBytes": 31333,
"brotliBytes": 27583,
"incrementalGzipBytes": 31333,
"incrementalBrotliBytes": 27583
"minifiedBytes": 83699,
"gzipBytes": 31398,
"brotliBytes": 27656,
"incrementalGzipBytes": 31398,
"incrementalBrotliBytes": 27656
},
"tanstack-area-basic": {
"minifiedBytes": 75923,
"gzipBytes": 28763,
"brotliBytes": 25453,
"incrementalGzipBytes": 28763,
"incrementalBrotliBytes": 25453
"minifiedBytes": 76136,
"gzipBytes": 28832,
"brotliBytes": 25473,
"incrementalGzipBytes": 28832,
"incrementalBrotliBytes": 25473
},
"tanstack-area-interactive": {
"minifiedBytes": 81098,
"gzipBytes": 30458,
"brotliBytes": 26812,
"incrementalGzipBytes": 30458,
"incrementalBrotliBytes": 26812
"minifiedBytes": 81311,
"gzipBytes": 30514,
"brotliBytes": 26891,
"incrementalGzipBytes": 30514,
"incrementalBrotliBytes": 26891
},
"tanstack-area-advanced": {
"minifiedBytes": 88465,
"gzipBytes": 32852,
"brotliBytes": 28903,
"incrementalGzipBytes": 32852,
"incrementalBrotliBytes": 28903
"minifiedBytes": 88678,
"gzipBytes": 32908,
"brotliBytes": 29053,
"incrementalGzipBytes": 32908,
"incrementalBrotliBytes": 29053
},
"tanstack-scatter-basic": {
"minifiedBytes": 71819,
"gzipBytes": 27220,
"brotliBytes": 24141,
"incrementalGzipBytes": 27220,
"incrementalBrotliBytes": 24141
"minifiedBytes": 72032,
"gzipBytes": 27293,
"brotliBytes": 24225,
"incrementalGzipBytes": 27293,
"incrementalBrotliBytes": 24225
},
"tanstack-scatter-interactive": {
"minifiedBytes": 76990,
"gzipBytes": 28892,
"brotliBytes": 25445,
"incrementalGzipBytes": 28892,
"incrementalBrotliBytes": 25445
"minifiedBytes": 77203,
"gzipBytes": 28957,
"brotliBytes": 25489,
"incrementalGzipBytes": 28957,
"incrementalBrotliBytes": 25489
},
"tanstack-scatter-advanced": {
"minifiedBytes": 77006,
"gzipBytes": 28897,
"brotliBytes": 25468,
"incrementalGzipBytes": 28897,
"incrementalBrotliBytes": 25468
"minifiedBytes": 77219,
"gzipBytes": 28962,
"brotliBytes": 25534,
"incrementalGzipBytes": 28962,
"incrementalBrotliBytes": 25534
},
"chartjs-line-basic": {
"minifiedBytes": 137909,
Expand Down
8 changes: 4 additions & 4 deletions docs/comparison.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,14 +12,14 @@ evidence without turning untested behavior into a checkmark.

| Library | Package | Measured source |
| -------------------------------------------------------------------------------------- | -------------------- | ------------------- |
| [TanStack Charts](./overview.md) | `@tanstack/charts` | workspace `c422a2c` |
| [TanStack Charts](./overview.md) | `@tanstack/charts` | workspace `d563eba` |
| [Chart.js](https://www.chartjs.org/docs/latest/) | `chart.js` | npm `4.5.1` |
| [Apache ECharts](https://echarts.apache.org/handbook/en/best-practices/canvas-vs-svg/) | `echarts` | npm `6.1.0` |
| [Recharts](https://recharts.github.io/en-US/) | `recharts` | npm `3.10.1` |
| [Observable Plot](https://observablehq.com/plot/features/plots) | `@observablehq/plot` | npm `0.6.17` |

The competitor versions are exact package pins, not latest versions inferred
at page render time. The measured TanStack workspace revision is `c422a2c`.
at page render time. The measured TanStack workspace revision is `d563eba`.

## Capability matrix

Expand Down Expand Up @@ -90,7 +90,7 @@ output model.

## Bundle snapshot

Baseline date: `2026-07-31`.
Baseline date: `2026-08-01`.

Controlled ranges cover 12 independently built, minified browser consumers:
line, bar, area, and scatter at basic, interactive, and advanced tiers. Only
Expand All @@ -106,7 +106,7 @@ Vega-Lite, AG Charts, and uPlot main exports were read from Bundlephobia on July

| Library | Bundle size | React externalized | Evidence |
| ------------------ | -------------------------------------- | -----------------: | ---------------------------------------------------------- |
| TanStack Charts | 26.58–32.08 KiB | — | Controlled suite |
| TanStack Charts | 26.65–32.14 KiB | — | Controlled suite |
| D3 | 90 KB gzip | — | External main export |
| Chart.js | 44.70–58.21 KiB | — | Controlled suite |
| Apache ECharts | 153.10–173.18 KiB | — | Controlled suite |
Expand Down
10 changes: 6 additions & 4 deletions docs/guides/tooltips-and-focus.md
Original file line number Diff line number Diff line change
Expand Up @@ -99,10 +99,12 @@ const definition = defineChart({
```

Array order is row order. A nullish field or `text` result omits the row.
Grouped focus keeps its shared-axis heading and series rows; order those rows
with `sort: 'color-domain'`, `sort: 'focus'`, or a typed comparator. Use
channel items to format their heading, series names, and values. Use `content`
when a grouped tooltip needs additional columns or nested sections.
Grouped focus keeps its shared-axis heading and series rows. By default, rows
follow the marks top-to-bottom for an x-group and left-to-right for a y-group.
Override that with `sort: 'color-domain'`, `sort: 'focus'`, or a typed
comparator. Use channel items to format their heading, series names, and values.
Use `content` when a grouped tooltip needs additional columns or nested
sections.

Customize plaintext content with typed formatters:

Expand Down
6 changes: 4 additions & 2 deletions docs/reference/focus-and-interaction.md
Original file line number Diff line number Diff line change
Expand Up @@ -138,7 +138,7 @@ interface ChartTooltipOptions<
| `className` | None | Class appended after `ts-chart-tooltip` |
| `portal` | None | Optional top-layer or fixed-position transport |
| `items` | Automatic x/y | Ordered rows for a single focused point |
| `sort` | `color-domain` | Grouped row order |
| `sort` | `visual` | Grouped row order |
| `anchor` | `point` | Preset, per-axis coordinates, or coordinate resolver |
| `placement` | `auto` | Fixed or ordered fallback box placements |
| `offset` | `10` | Scene-pixel gap between anchor and box |
Expand Down Expand Up @@ -191,7 +191,9 @@ label and text, the opposite-axis item formats values, and the group item
formats series names. `sort` orders those generated series rows. Additional
grouped structure belongs in `content`.

`sort` accepts `color-domain`, `focus`, or a typed point comparator.
`sort` accepts `visual`, `color-domain`, `focus`, or a typed point comparator.
Visual order follows the marks across the screen: top-to-bottom for an x-group
and left-to-right for a y-group.

### Anchor and placement

Expand Down
8 changes: 4 additions & 4 deletions packages/charts-core/docs/comparison.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,14 +12,14 @@ evidence without turning untested behavior into a checkmark.

| Library | Package | Measured source |
| -------------------------------------------------------------------------------------- | -------------------- | ------------------- |
| [TanStack Charts](./overview.md) | `@tanstack/charts` | workspace `c422a2c` |
| [TanStack Charts](./overview.md) | `@tanstack/charts` | workspace `d563eba` |
| [Chart.js](https://www.chartjs.org/docs/latest/) | `chart.js` | npm `4.5.1` |
| [Apache ECharts](https://echarts.apache.org/handbook/en/best-practices/canvas-vs-svg/) | `echarts` | npm `6.1.0` |
| [Recharts](https://recharts.github.io/en-US/) | `recharts` | npm `3.10.1` |
| [Observable Plot](https://observablehq.com/plot/features/plots) | `@observablehq/plot` | npm `0.6.17` |

The competitor versions are exact package pins, not latest versions inferred
at page render time. The measured TanStack workspace revision is `c422a2c`.
at page render time. The measured TanStack workspace revision is `d563eba`.

## Capability matrix

Expand Down Expand Up @@ -90,7 +90,7 @@ output model.

## Bundle snapshot

Baseline date: `2026-07-31`.
Baseline date: `2026-08-01`.

Controlled ranges cover 12 independently built, minified browser consumers:
line, bar, area, and scatter at basic, interactive, and advanced tiers. Only
Expand All @@ -106,7 +106,7 @@ Vega-Lite, AG Charts, and uPlot main exports were read from Bundlephobia on July

| Library | Bundle size | React externalized | Evidence |
| ------------------ | -------------------------------------- | -----------------: | ---------------------------------------------------------- |
| TanStack Charts | 26.58–32.08 KiB | — | Controlled suite |
| TanStack Charts | 26.65–32.14 KiB | — | Controlled suite |
| D3 | 90 KB gzip | — | External main export |
| Chart.js | 44.70–58.21 KiB | — | Controlled suite |
| Apache ECharts | 153.10–173.18 KiB | — | Controlled suite |
Expand Down
10 changes: 6 additions & 4 deletions packages/charts-core/docs/guides/tooltips-and-focus.md
Original file line number Diff line number Diff line change
Expand Up @@ -99,10 +99,12 @@ const definition = defineChart({
```

Array order is row order. A nullish field or `text` result omits the row.
Grouped focus keeps its shared-axis heading and series rows; order those rows
with `sort: 'color-domain'`, `sort: 'focus'`, or a typed comparator. Use
channel items to format their heading, series names, and values. Use `content`
when a grouped tooltip needs additional columns or nested sections.
Grouped focus keeps its shared-axis heading and series rows. By default, rows
follow the marks top-to-bottom for an x-group and left-to-right for a y-group.
Override that with `sort: 'color-domain'`, `sort: 'focus'`, or a typed
comparator. Use channel items to format their heading, series names, and values.
Use `content` when a grouped tooltip needs additional columns or nested
sections.

Customize plaintext content with typed formatters:

Expand Down
6 changes: 4 additions & 2 deletions packages/charts-core/docs/reference/focus-and-interaction.md
Original file line number Diff line number Diff line change
Expand Up @@ -138,7 +138,7 @@ interface ChartTooltipOptions<
| `className` | None | Class appended after `ts-chart-tooltip` |
| `portal` | None | Optional top-layer or fixed-position transport |
| `items` | Automatic x/y | Ordered rows for a single focused point |
| `sort` | `color-domain` | Grouped row order |
| `sort` | `visual` | Grouped row order |

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win

Use the documentation synchronization workflow for both generated copies.

Apply the public documentation changes only in the corresponding files under docs/, then run pnpm docs:sync.

  • packages/charts-core/docs/reference/focus-and-interaction.md#L141-L141: regenerate the default sort table entry.
  • packages/charts-core/docs/reference/focus-and-interaction.md#L194-L196: regenerate the visual-ordering policy description.
  • packages/charts-core/docs/guides/tooltips-and-focus.md#L102-L107: regenerate the grouped-ordering guidance.

As per coding guidelines, docs/**: Author public documentation only in the root docs/ tree; do not directly edit packages/charts-core/docs or either llms.txt file because those copies are generated by pnpm docs:sync.

📍 Affects 2 files
  • packages/charts-core/docs/reference/focus-and-interaction.md#L141-L141 (this comment)
  • packages/charts-core/docs/reference/focus-and-interaction.md#L194-L196
  • packages/charts-core/docs/guides/tooltips-and-focus.md#L102-L107
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/charts-core/docs/reference/focus-and-interaction.md` at line 141,
Update the corresponding public documentation in the root docs/ tree, not the
generated package copies: regenerate the default sort table entry,
visual-ordering policy description, and grouped-ordering guidance corresponding
to packages/charts-core/docs/reference/focus-and-interaction.md lines 141 and
194-196 and packages/charts-core/docs/guides/tooltips-and-focus.md lines
102-107, then run pnpm docs:sync to update all generated copies.

Source: Coding guidelines

| `anchor` | `point` | Preset, per-axis coordinates, or coordinate resolver |
| `placement` | `auto` | Fixed or ordered fallback box placements |
| `offset` | `10` | Scene-pixel gap between anchor and box |
Expand Down Expand Up @@ -191,7 +191,9 @@ label and text, the opposite-axis item formats values, and the group item
formats series names. `sort` orders those generated series rows. Additional
grouped structure belongs in `content`.

`sort` accepts `color-domain`, `focus`, or a typed point comparator.
`sort` accepts `visual`, `color-domain`, `focus`, or a typed point comparator.
Visual order follows the marks across the screen: top-to-bottom for an x-group
and left-to-right for a y-group.

### Anchor and placement

Expand Down
Loading