From e4b5249ceca9910d990aefc9f8777a8d28e63921 Mon Sep 17 00:00:00 2001 From: Tanner Linsley Date: Sun, 2 Aug 2026 14:24:48 -0600 Subject: [PATCH 1/2] fix: preserve focus ring with authored focus marks --- .changeset/steady-focus-rings.md | 6 +++ API-FRICTION.md | 22 ++++++++++ .../bundle-size/universal-baseline.json | 40 +++++++++---------- docs/reference/chart-definitions.md | 6 +-- docs/reference/dom-host.md | 1 + docs/reference/focus-and-interaction.md | 3 ++ .../src/InteractionGeometryLab.test.ts | 22 ++++++---- .../docs/reference/chart-definitions.md | 6 +-- .../charts-core/docs/reference/dom-host.md | 1 + .../docs/reference/focus-and-interaction.md | 3 ++ packages/charts-core/src/canvas.test.ts | 27 ++++++++++++- packages/charts-core/src/facet.test.ts | 4 +- packages/charts-core/src/focus-mark.test.ts | 36 ++++++++++++++++- packages/charts-core/src/scene.ts | 12 +----- packages/charts-core/src/types.ts | 3 ++ 15 files changed, 141 insertions(+), 51 deletions(-) create mode 100644 .changeset/steady-focus-rings.md diff --git a/.changeset/steady-focus-rings.md b/.changeset/steady-focus-rings.md new file mode 100644 index 00000000..4266f41f --- /dev/null +++ b/.changeset/steady-focus-rings.md @@ -0,0 +1,6 @@ +--- +'@tanstack/charts': patch +--- + +Keep the built-in primary focus ring when authored focus marks are present, and +add `focusRing: false` for charts that replace the indicator explicitly. diff --git a/API-FRICTION.md b/API-FRICTION.md index 8b528bae..403be872 100644 --- a/API-FRICTION.md +++ b/API-FRICTION.md @@ -231,6 +231,7 @@ Each entry records: | F-193 | Fixed catalog height hid compact responsive examples | Tooling/App | resolved | | F-194 | Behavior runs omitted the interactive input | Tooling | resolved | | F-195 | Release versions matched dependency substrings | Tooling | resolved | +| F-196 | Focus decorations suppressed the primary indicator | API | resolved | ## Findings @@ -4868,3 +4869,24 @@ Each entry records: from 0.6.0 to 0.6.1 while leaving Observable Plot 0.6.17 unchanged. The release workflow can create the 0.6.1 version pull request from the same changeset. + +### F-196 — Focus decorations suppressed the primary indicator + +- Status: resolved +- Severity: high +- Owner: API +- Observed in: issue #33 focused-band composition +- Friction: adding any `whenFocused` mark removed the built-in primary-point + ring from the complete chart. A category highlight or crosshair therefore + also removed the indicator that identified the point owned by the tooltip + and keyboard focus. +- Decision: compose the built-in ring with authored focus layers by default. + Add definition `focusRing: false` for charts whose authored geometry + deliberately replaces the primary indicator. +- Verification: scene and SVG regressions retain both a focused category band + and one primary ring, Canvas paints authored underlays and the default + overlay together, facets retain synchronized authored bands plus one shared + ring, and an explicit opt-out omits the ring. Removing the implicit + focus-layer scan reduces the ten locked universal bundles by 109–110 + minified bytes and 22–48 gzip bytes; the reviewed exact baseline records the + decrease. diff --git a/benchmarks/bundle-size/universal-baseline.json b/benchmarks/bundle-size/universal-baseline.json index f1c659de..2ff87528 100644 --- a/benchmarks/bundle-size/universal-baseline.json +++ b/benchmarks/bundle-size/universal-baseline.json @@ -3,44 +3,44 @@ "policy": "Exact minified and gzip output for entries that optional features must not affect. Review every change before updating.", "bundles": { "D3-scale line scene": { - "bytes": 39555, - "gzip": 15385 + "bytes": 39446, + "gzip": 15338 }, "D3-scale line + static SVG": { - "bytes": 42496, - "gzip": 16463 + "bytes": 42387, + "gzip": 16429 }, "Representative marks": { - "bytes": 58285, - "gzip": 21632 + "bytes": 58176, + "gzip": 21599 }, "TanStack DOM host": { - "bytes": 44570, - "gzip": 16278 + "bytes": 44461, + "gzip": 16247 }, "React adapter": { - "bytes": 46738, - "gzip": 16971 + "bytes": 46629, + "gzip": 16949 }, "React line consumer": { - "bytes": 69404, - "gzip": 26052 + "bytes": 69295, + "gzip": 26026 }, "Compact-scale line scene": { - "bytes": 22019, - "gzip": 8326 + "bytes": 21910, + "gzip": 8283 }, "React compact-scale line consumer": { - "bytes": 51915, - "gzip": 18954 + "bytes": 51805, + "gzip": 18924 }, "Custom-scale line scene": { - "bytes": 20204, - "gzip": 7581 + "bytes": 20094, + "gzip": 7544 }, "D3 linear-scale line scene": { - "bytes": 39487, - "gzip": 15349 + "bytes": 39378, + "gzip": 15301 } } } diff --git a/docs/reference/chart-definitions.md b/docs/reference/chart-definitions.md index ab2999ee..05026a3c 100644 --- a/docs/reference/chart-definitions.md +++ b/docs/reference/chart-definitions.md @@ -87,9 +87,9 @@ does not return or own them. ## Definition behavior `ChartDefinitionOptions` contains `focus`, -`maxFocusDistance`, `spatialIndex`, `animate`, `keyboard`, and `tooltip`. -These options belong to both static and responsive definitions. Hosts and -framework adapters do not override them. +`focusRing`, `maxFocusDistance`, `spatialIndex`, `animate`, `keyboard`, and +`tooltip`. These options belong to both static and responsive definitions. +Hosts and framework adapters do not override them. Tooltip placement policy stays with the definition. Add the `portal` extension when the surface must escape clipped chart ancestors. Framework-only content diff --git a/docs/reference/dom-host.md b/docs/reference/dom-host.md index c8beb75b..5d0b9919 100644 --- a/docs/reference/dom-host.md +++ b/docs/reference/dom-host.md @@ -116,6 +116,7 @@ The definition owns these chart behaviors: | ------------------ | ------------------------- | -------------------------------------------------------- | | `maxFocusDistance` | `48` | Maximum scene-pixel distance for default pointer focus | | `focus` | Nearest point | Pointer grouping and keyboard navigation strategy | +| `focusRing` | `true` | Built-in primary-point focus indicator | | `spatialIndex` | Linear nearest-point scan | Dense-data nearest-point index | | `animate` | `false` | Keyed attribute, enter, and exit animation | | `keyboard` | `true` | Keyboard focus and navigation | diff --git a/docs/reference/focus-and-interaction.md b/docs/reference/focus-and-interaction.md index a9273aca..1232f700 100644 --- a/docs/reference/focus-and-interaction.md +++ b/docs/reference/focus-and-interaction.md @@ -27,6 +27,9 @@ With no custom focus strategy: `maxFocusDistance` defaults to `48` scene pixels. Set `tabIndex` to control normal tab-order participation while keeping keyboard handling enabled. Set `keyboard: false` to remove keyboard navigation and force tab index `-1`. +Authored `whenFocused` marks compose with the primary-point ring. Set +definition `focusRing: false` only when authored focus geometry replaces that +indicator. ### Interaction geometry diff --git a/examples/sandbox/src/InteractionGeometryLab.test.ts b/examples/sandbox/src/InteractionGeometryLab.test.ts index 969767da..148c18a0 100644 --- a/examples/sandbox/src/InteractionGeometryLab.test.ts +++ b/examples/sandbox/src/InteractionGeometryLab.test.ts @@ -157,14 +157,20 @@ describe('intentional interaction contracts', () => { expect(primaryLayers.every((node) => node.focus?.match === 'primary')).toBe( true, ) - expect(synchronizedXLayers).toHaveLength(2) - expect(synchronizedXLayers.every((node) => node.focus?.match === 'x')).toBe( - true, - ) - expect(synchronizedYLayers).toHaveLength(2) - expect(synchronizedYLayers.every((node) => node.focus?.match === 'y')).toBe( - true, - ) + expect(synchronizedXLayers).toHaveLength(3) + expect( + synchronizedXLayers.filter((node) => node.focus?.match === 'x'), + ).toHaveLength(2) + expect( + synchronizedXLayers.filter((node) => node.focus?.match === 'primary'), + ).toHaveLength(1) + expect(synchronizedYLayers).toHaveLength(3) + expect( + synchronizedYLayers.filter((node) => node.focus?.match === 'y'), + ).toHaveLength(2) + expect( + synchronizedYLayers.filter((node) => node.focus?.match === 'primary'), + ).toHaveLength(1) }) it('keeps the animated case on a deliberately tight fallback threshold', () => { diff --git a/packages/charts-core/docs/reference/chart-definitions.md b/packages/charts-core/docs/reference/chart-definitions.md index ab2999ee..05026a3c 100644 --- a/packages/charts-core/docs/reference/chart-definitions.md +++ b/packages/charts-core/docs/reference/chart-definitions.md @@ -87,9 +87,9 @@ does not return or own them. ## Definition behavior `ChartDefinitionOptions` contains `focus`, -`maxFocusDistance`, `spatialIndex`, `animate`, `keyboard`, and `tooltip`. -These options belong to both static and responsive definitions. Hosts and -framework adapters do not override them. +`focusRing`, `maxFocusDistance`, `spatialIndex`, `animate`, `keyboard`, and +`tooltip`. These options belong to both static and responsive definitions. +Hosts and framework adapters do not override them. Tooltip placement policy stays with the definition. Add the `portal` extension when the surface must escape clipped chart ancestors. Framework-only content diff --git a/packages/charts-core/docs/reference/dom-host.md b/packages/charts-core/docs/reference/dom-host.md index c8beb75b..5d0b9919 100644 --- a/packages/charts-core/docs/reference/dom-host.md +++ b/packages/charts-core/docs/reference/dom-host.md @@ -116,6 +116,7 @@ The definition owns these chart behaviors: | ------------------ | ------------------------- | -------------------------------------------------------- | | `maxFocusDistance` | `48` | Maximum scene-pixel distance for default pointer focus | | `focus` | Nearest point | Pointer grouping and keyboard navigation strategy | +| `focusRing` | `true` | Built-in primary-point focus indicator | | `spatialIndex` | Linear nearest-point scan | Dense-data nearest-point index | | `animate` | `false` | Keyed attribute, enter, and exit animation | | `keyboard` | `true` | Keyboard focus and navigation | diff --git a/packages/charts-core/docs/reference/focus-and-interaction.md b/packages/charts-core/docs/reference/focus-and-interaction.md index a9273aca..1232f700 100644 --- a/packages/charts-core/docs/reference/focus-and-interaction.md +++ b/packages/charts-core/docs/reference/focus-and-interaction.md @@ -27,6 +27,9 @@ With no custom focus strategy: `maxFocusDistance` defaults to `48` scene pixels. Set `tabIndex` to control normal tab-order participation while keeping keyboard handling enabled. Set `keyboard: false` to remove keyboard navigation and force tab index `-1`. +Authored `whenFocused` marks compose with the primary-point ring. Set +definition `focusRing: false` only when authored focus geometry replaces that +indicator. ### Interaction geometry diff --git a/packages/charts-core/src/canvas.test.ts b/packages/charts-core/src/canvas.test.ts index 3acafdf0..84dadd3f 100644 --- a/packages/charts-core/src/canvas.test.ts +++ b/packages/charts-core/src/canvas.test.ts @@ -370,7 +370,7 @@ describe('Canvas renderer', () => { surface.destroy() }) - it('paints leading focus marks below the cached base scene', () => { + it('composes authored focus underlays with the default indicator', () => { const container = document.createElement('div') const surface = createCanvasChartRenderer().mount(container, () => {}) const datum = { id: 'band' } @@ -417,12 +417,34 @@ describe('Canvas renderer', () => { radius: 5, style: { fill: '#ff0000' }, }, + { + kind: 'group', + key: 'default-focus', + children: [ + { + kind: 'dot', + key: 'dot', + x: 25, + y: 30, + radius: 5, + style: { stroke: '#ff0000', strokeWidth: 2.5 }, + }, + ], + focus: { + match: 'primary', + points: [{ ...point, key: 'dot', markId: 'dots' }], + placement: 'over', + }, + }, ]), renderOptions(), ) const base = contexts.get(surface.canvas) const under = contexts.get(surface.focusUnderCanvas) - if (!base || !under) throw new Error('Expected base and underlay contexts') + const over = contexts.get(surface.focusCanvas) + if (!base || !under || !over) { + throw new Error('Expected base and focus contexts') + } const baseOperations = [...base.operations] surface.paintFocus({ @@ -438,6 +460,7 @@ describe('Canvas renderer', () => { expect(base.operations).toEqual(baseOperations) expect(under.operations).toContain('rect:10,0,30,60') + expect(over.operations).toContain('arc:25,30,5') surface.destroy() }) diff --git a/packages/charts-core/src/facet.test.ts b/packages/charts-core/src/facet.test.ts index 2a0604ec..7048c4e7 100644 --- a/packages/charts-core/src/facet.test.ts +++ b/packages/charts-core/src/facet.test.ts @@ -457,7 +457,7 @@ describe('facets', () => { const visibleBands = visibleLayers.flatMap((layer) => [ ...layer.querySelectorAll('rect[visibility="visible"]'), ]) - expect(visibleLayers).toHaveLength(2) + expect(visibleLayers).toHaveLength(3) expect(visibleBands).toHaveLength(2) expect( new Set(visibleBands.map((band) => band.getAttribute('x'))).size, @@ -518,7 +518,7 @@ describe('facets', () => { const visibleBands = visibleLayers.flatMap((layer) => [ ...layer.querySelectorAll('rect[visibility="visible"]'), ]) - expect(visibleLayers).toHaveLength(2) + expect(visibleLayers).toHaveLength(3) expect(visibleBands).toHaveLength(2) expect( visibleBands.every((band) => Number(band.getAttribute('height')) > 0), diff --git a/packages/charts-core/src/focus-mark.test.ts b/packages/charts-core/src/focus-mark.test.ts index cbb2c585..521e7cda 100644 --- a/packages/charts-core/src/focus-mark.test.ts +++ b/packages/charts-core/src/focus-mark.test.ts @@ -69,7 +69,7 @@ describe('focus-filtered marks', () => { node.kind === 'group' && node.className?.includes('focus-layer--default'), ), - ).toBe(false) + ).toBe(true) }) it('shows only ordinary mark geometry matching the focused x value', () => { @@ -89,7 +89,9 @@ describe('focus-filtered marks', () => { pinned: false, }) - const layer = container.querySelector('[data-ts-focus-layer]') + const layer = container.querySelector( + '.ts-chart__marks [data-ts-focus-layer]', + ) expect(layer?.getAttribute('visibility')).toBe('visible') const visibleRects = [...(layer?.querySelectorAll('rect') ?? [])].filter( (rect) => rect.getAttribute('visibility') === 'visible', @@ -99,10 +101,40 @@ describe('focus-filtered marks', () => { visibleRects.every((rect) => Number(rect.getAttribute('height')) > 0), ).toBe(true) + const defaultLayer = container.querySelector( + '.ts-chart__focus-layer--default', + ) + expect(defaultLayer?.getAttribute('visibility')).toBe('visible') + expect( + defaultLayer?.querySelectorAll('circle[visibility="visible"]'), + ).toHaveLength(1) + surface.paintFocus(null) expect(layer?.getAttribute('visibility')).toBe('hidden') + expect(defaultLayer?.getAttribute('visibility')).toBe('hidden') surface.destroy() }) + + it('allows the built-in primary-point ring to be disabled explicitly', () => { + const rows = [{ category: 'A', value: 12 }] + const resolved = createChartScene( + defineChart({ + marks: [barY(rows, { x: 'category', y: 'value' })], + x: { scale: scaleBand().domain(['A']) }, + y: { scale: scaleLinear().domain([0, 20]) }, + focusRing: false, + }), + { width: 240, height: 160 }, + ) + + expect( + resolved.nodes.some( + (node) => + node.kind === 'group' && + node.className?.includes('focus-layer--default'), + ), + ).toBe(false) + }) }) describe('inline mark states', () => { diff --git a/packages/charts-core/src/scene.ts b/packages/charts-core/src/scene.ts index c1db079c..e0f99bba 100644 --- a/packages/charts-core/src/scene.ts +++ b/packages/charts-core/src/scene.ts @@ -208,7 +208,6 @@ function createChartSceneWithScaleResolver< const markNodes: SceneNode[] = [] const points: ChartPoint[] = [] const firstBaseMarkIndex = initialized.findIndex((mark) => !mark.focus) - let hasFocusLayers = initialized.some((mark) => mark.focus) initialized.forEach((mark, markIndex) => { const rendered = mark.render({ @@ -224,7 +223,6 @@ function createChartSceneWithScaleResolver< rendered.nodes, rendered.points, ) - hasFocusLayers ||= containsFocusLayer(rendered.nodes) if (mark.focus) { markNodes.push({ kind: 'group', @@ -283,7 +281,7 @@ function createChartSceneWithScaleResolver< nodes.push(axisNodes) } if (legend) nodes.push(legend.render({ colors, chart, theme, width })) - if (!hasFocusLayers && points.length) { + if (definition.focusRing !== false && points.length) { nodes.push({ kind: 'group', key: 'default-focus', @@ -374,14 +372,6 @@ function collectRenderedPoints( return points } -function containsFocusLayer(nodes: readonly SceneNode[]): boolean { - return nodes.some( - (node) => - node.kind === 'group' && - (node.focus !== undefined || containsFocusLayer(node.children)), - ) -} - function collectScaleChannels( marks: readonly InitializedMark[], scaleId: string, diff --git a/packages/charts-core/src/types.ts b/packages/charts-core/src/types.ts index 65665f00..ec68e142 100644 --- a/packages/charts-core/src/types.ts +++ b/packages/charts-core/src/types.ts @@ -602,6 +602,8 @@ export interface ChartDefinitionOptions< > { maxFocusDistance?: number focus?: ChartFocusMode, NoInfer, NoInfer> + /** Shows the built-in primary-point focus ring. Defaults to true. */ + focusRing?: boolean spatialIndex?: ChartSpatialIndexFactory animate?: boolean | ChartAnimationOptions /** Renderer-neutral motion defaults. An optional motion implementation consumes them. */ @@ -615,6 +617,7 @@ export interface ChartDefinitionOptions< interface StoredChartDefinitionOptions { maxFocusDistance?: number focus?: ChartFocusMode + focusRing?: boolean spatialIndex?: ChartSpatialIndexFactory animate?: boolean | ChartAnimationOptions motion?: ChartMotionDefinition From 5904af79e34ab0ee36b9ccbb75f1fb3f04e2d35e Mon Sep 17 00:00:00 2001 From: Tanner Linsley Date: Sun, 2 Aug 2026 15:07:05 -0600 Subject: [PATCH 2/2] chore: refresh focus comparison evidence --- API-FRICTION.md | 18 +++ benchmarks/comparison/bundle-baseline.json | 126 ++++++++++----------- docs/comparison.md | 6 +- packages/charts-core/docs/comparison.md | 6 +- 4 files changed, 87 insertions(+), 69 deletions(-) diff --git a/API-FRICTION.md b/API-FRICTION.md index 403be872..9d83ce5c 100644 --- a/API-FRICTION.md +++ b/API-FRICTION.md @@ -232,6 +232,7 @@ Each entry records: | F-194 | Behavior runs omitted the interactive input | Tooling | resolved | | F-195 | Release versions matched dependency substrings | Tooling | resolved | | F-196 | Focus decorations suppressed the primary indicator | API | resolved | +| F-197 | Workspace validation omitted comparison provenance | Tooling | resolved | ## Findings @@ -4890,3 +4891,20 @@ Each entry records: focus-layer scan reduces the ten locked universal bundles by 109–110 minified bytes and 22–48 gzip bytes; the reviewed exact baseline records the decrease. + +### F-197 — Workspace validation omitted comparison provenance + +- Status: resolved +- Severity: low +- Owner: Tooling +- Observed in: issue #33 release validation +- Friction: `pnpm run validate` passed the locked universal bundle policy but + did not run the separate comparison baseline gate. Because the focus fix + changed a core comparison input, pull-request CI correctly rejected the + stale workspace revision even though local validation was green. +- Decision: refresh the comparison evidence whenever a change advances one of + its tracked TanStack inputs, and run `pnpm benchmark:check` alongside the + workspace validation before release. +- Verification: the 60-case comparison baseline records revision `e4b5249` + and package version 0.6.2, the canonical comparison page identifies the same + revision, and `pnpm benchmark:check` passes locally and in pull-request CI. diff --git a/benchmarks/comparison/bundle-baseline.json b/benchmarks/comparison/bundle-baseline.json index dfe09603..82966ae9 100644 --- a/benchmarks/comparison/bundle-baseline.json +++ b/benchmarks/comparison/bundle-baseline.json @@ -1,8 +1,8 @@ { "schemaVersion": 3, - "generatedAt": "2026-08-02T20:16:51.564Z", + "generatedAt": "2026-08-02T21:03:04.476Z", "packageVersions": { - "tanstack": "0.6.1", + "tanstack": "0.6.2", "chartjs": "4.5.1", "echarts": "6.1.0", "recharts": "3.10.1", @@ -11,7 +11,7 @@ "sources": { "tanstack": { "kind": "workspace", - "revision": "0eeec7dbe62d362839820b953a731ae0596206ee" + "revision": "e4b5249ceca9910d990aefc9f8777a8d28e63921" }, "chartjs": { "kind": "package", @@ -44,88 +44,88 @@ }, "bundles": { "tanstack-line-basic": { - "minifiedBytes": 78211, - "gzipBytes": 29465, - "brotliBytes": 26139, - "incrementalGzipBytes": 29465, - "incrementalBrotliBytes": 26139 + "minifiedBytes": 78102, + "gzipBytes": 29435, + "brotliBytes": 26049, + "incrementalGzipBytes": 29435, + "incrementalBrotliBytes": 26049 }, "tanstack-line-interactive": { - "minifiedBytes": 83382, - "gzipBytes": 31104, - "brotliBytes": 27459, - "incrementalGzipBytes": 31104, - "incrementalBrotliBytes": 27459 + "minifiedBytes": 83273, + "gzipBytes": 31071, + "brotliBytes": 27438, + "incrementalGzipBytes": 31071, + "incrementalBrotliBytes": 27438 }, "tanstack-line-advanced": { - "minifiedBytes": 90567, - "gzipBytes": 33457, - "brotliBytes": 29507, - "incrementalGzipBytes": 33457, - "incrementalBrotliBytes": 29507 + "minifiedBytes": 90458, + "gzipBytes": 33429, + "brotliBytes": 29450, + "incrementalGzipBytes": 33429, + "incrementalBrotliBytes": 29450 }, "tanstack-bar-basic": { - "minifiedBytes": 85222, - "gzipBytes": 32128, - "brotliBytes": 28411, - "incrementalGzipBytes": 32128, - "incrementalBrotliBytes": 28411 + "minifiedBytes": 85113, + "gzipBytes": 32109, + "brotliBytes": 28367, + "incrementalGzipBytes": 32109, + "incrementalBrotliBytes": 28367 }, "tanstack-bar-interactive": { - "minifiedBytes": 89248, - "gzipBytes": 33326, - "brotliBytes": 29367, - "incrementalGzipBytes": 33326, - "incrementalBrotliBytes": 29367 + "minifiedBytes": 89139, + "gzipBytes": 33306, + "brotliBytes": 29384, + "incrementalGzipBytes": 33306, + "incrementalBrotliBytes": 29384 }, "tanstack-bar-advanced": { - "minifiedBytes": 89587, - "gzipBytes": 33458, - "brotliBytes": 29488, - "incrementalGzipBytes": 33458, - "incrementalBrotliBytes": 29488 + "minifiedBytes": 89478, + "gzipBytes": 33449, + "brotliBytes": 29510, + "incrementalGzipBytes": 33449, + "incrementalBrotliBytes": 29510 }, "tanstack-area-basic": { - "minifiedBytes": 82020, - "gzipBytes": 30953, - "brotliBytes": 27423, - "incrementalGzipBytes": 30953, - "incrementalBrotliBytes": 27423 + "minifiedBytes": 81911, + "gzipBytes": 30928, + "brotliBytes": 27358, + "incrementalGzipBytes": 30928, + "incrementalBrotliBytes": 27358 }, "tanstack-area-interactive": { - "minifiedBytes": 87199, - "gzipBytes": 32637, - "brotliBytes": 28802, - "incrementalGzipBytes": 32637, - "incrementalBrotliBytes": 28802 + "minifiedBytes": 87090, + "gzipBytes": 32608, + "brotliBytes": 28679, + "incrementalGzipBytes": 32608, + "incrementalBrotliBytes": 28679 }, "tanstack-area-advanced": { - "minifiedBytes": 94566, - "gzipBytes": 34929, - "brotliBytes": 30869, - "incrementalGzipBytes": 34929, - "incrementalBrotliBytes": 30869 + "minifiedBytes": 94457, + "gzipBytes": 34904, + "brotliBytes": 30844, + "incrementalGzipBytes": 34904, + "incrementalBrotliBytes": 30844 }, "tanstack-scatter-basic": { - "minifiedBytes": 77886, - "gzipBytes": 29367, - "brotliBytes": 26035, - "incrementalGzipBytes": 29367, - "incrementalBrotliBytes": 26035 + "minifiedBytes": 77777, + "gzipBytes": 29332, + "brotliBytes": 26021, + "incrementalGzipBytes": 29332, + "incrementalBrotliBytes": 26021 }, "tanstack-scatter-interactive": { - "minifiedBytes": 83057, - "gzipBytes": 31005, - "brotliBytes": 27357, - "incrementalGzipBytes": 31005, - "incrementalBrotliBytes": 27357 + "minifiedBytes": 82948, + "gzipBytes": 30973, + "brotliBytes": 27313, + "incrementalGzipBytes": 30973, + "incrementalBrotliBytes": 27313 }, "tanstack-scatter-advanced": { - "minifiedBytes": 83073, - "gzipBytes": 31010, - "brotliBytes": 27387, - "incrementalGzipBytes": 31010, - "incrementalBrotliBytes": 27387 + "minifiedBytes": 82964, + "gzipBytes": 30979, + "brotliBytes": 27318, + "incrementalGzipBytes": 30979, + "incrementalBrotliBytes": 27318 }, "chartjs-line-basic": { "minifiedBytes": 137909, diff --git a/docs/comparison.md b/docs/comparison.md index 74c10992..f47830ca 100644 --- a/docs/comparison.md +++ b/docs/comparison.md @@ -12,14 +12,14 @@ untested behavior into a checkmark. | Library | Package | Measured source | | -------------------------------------------------------------------------------------- | -------------------- | ------------------- | -| [TanStack Charts](./overview.md) | `@tanstack/charts` | workspace `0eeec7d` | +| [TanStack Charts](./overview.md) | `@tanstack/charts` | workspace `e4b5249` | | [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 `0eeec7d`. +at page render time. The measured TanStack workspace revision is `e4b5249`. ## Capability matrix @@ -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 | 28.68–34.11 KiB | — | Controlled suite | +| TanStack Charts | 28.64–34.09 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 | diff --git a/packages/charts-core/docs/comparison.md b/packages/charts-core/docs/comparison.md index 74c10992..f47830ca 100644 --- a/packages/charts-core/docs/comparison.md +++ b/packages/charts-core/docs/comparison.md @@ -12,14 +12,14 @@ untested behavior into a checkmark. | Library | Package | Measured source | | -------------------------------------------------------------------------------------- | -------------------- | ------------------- | -| [TanStack Charts](./overview.md) | `@tanstack/charts` | workspace `0eeec7d` | +| [TanStack Charts](./overview.md) | `@tanstack/charts` | workspace `e4b5249` | | [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 `0eeec7d`. +at page render time. The measured TanStack workspace revision is `e4b5249`. ## Capability matrix @@ -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 | 28.68–34.11 KiB | — | Controlled suite | +| TanStack Charts | 28.64–34.09 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 |