Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
39 commits
Select commit Hold shift + click to select a range
8e35f59
fix(benchmarks/conformance/cases): pin 'en-US' so catalog rendering i…
sukvvon Aug 8, 2026
89ca644
Allow line marks to configure caps and joins
wojtekmaj Aug 27, 2026
86899fa
Add Cartesian axis title styling
wojtekmaj Aug 27, 2026
0ae09c6
Allow configuring focus ring appearance
wojtekmaj Aug 27, 2026
7067223
Preserve focus ring defaults for undefined options
wojtekmaj Aug 27, 2026
8024dd2
Reserve the axis gutter in a right-to-left container
3li7alaki Aug 27, 2026
7035bde
Relock the difference-mark bundle increment
3li7alaki Aug 27, 2026
4a1b3d1
Harden focus styling and RTL text anchors
tannerlinsley Sep 2, 2026
699b4be
Complete RTL text direction support
tannerlinsley Sep 2, 2026
5e2bfcc
Update combined release baselines
tannerlinsley Sep 2, 2026
554e356
Address final review findings
tannerlinsley Sep 3, 2026
e4e06bb
Add configurable wheel activation to zoomX
tannerlinsley Sep 3, 2026
2a0f819
Merge remote-tracking branch 'origin/main' into taren/pr-68-locale
tannerlinsley Sep 3, 2026
f2b9b3f
Make catalog rendering locale-independent
tannerlinsley Sep 3, 2026
57056aa
Merge main into fix-open-issues
tannerlinsley Sep 9, 2026
8e45143
Refresh PR 124 evidence on v0.17.0
tannerlinsley Sep 9, 2026
7017ad5
Merge main into modifier-wheel-zoom
tannerlinsley Sep 9, 2026
947d6e5
Refresh PR 125 evidence on v0.17.0
tannerlinsley Sep 9, 2026
4c25bb1
Sanitize XML-invalid SVG characters
tannerlinsley Sep 9, 2026
1ff2b12
Reduce XML sanitizer footprint
tannerlinsley Sep 9, 2026
82ede97
Escape custom SVG paths
tannerlinsley Sep 9, 2026
18645d4
Refresh PR 124 evidence after XML sanitization
tannerlinsley Sep 9, 2026
35fbde9
Fix retargeted focus group motion
tannerlinsley Sep 9, 2026
635c3de
Merge current main into catalog locale fix
tannerlinsley Sep 9, 2026
f175e63
Add renderer-neutral radial gradients
tannerlinsley Sep 9, 2026
9a3cf58
Add static guide line styles
tannerlinsley Sep 10, 2026
ebefef6
Merge branch 'main' into taren/fix-open-issues
tannerlinsley Sep 10, 2026
7003668
Integrate locale fixes with chart styling
tannerlinsley Sep 10, 2026
0785850
Refresh chart styling against merged viewport and documentation fixes
tannerlinsley Sep 10, 2026
3eaaf93
Refresh locale fixes against cumulative release changes
tannerlinsley Sep 10, 2026
f136bbf
Integrate wheel activation with cumulative release changes
tannerlinsley Sep 10, 2026
8e3dffd
Record cumulative wheel release measurements
tannerlinsley Sep 10, 2026
32ce41d
Integrate focus motion with cumulative release changes
tannerlinsley Sep 10, 2026
77c7a63
Record cumulative focus motion measurements
tannerlinsley Sep 10, 2026
9e73e9d
Integrate radial gradients with cumulative release changes
tannerlinsley Sep 10, 2026
2e038b0
Record cumulative radial gradient measurements
tannerlinsley Sep 10, 2026
87f5984
Integrate guide styling and radial stroke handling
tannerlinsley Sep 10, 2026
f1f72a8
Record cumulative guide styling measurements
tannerlinsley Sep 10, 2026
00469ba
Reconcile merged radial gradient history without changing release source
tannerlinsley Sep 10, 2026
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
5 changes: 5 additions & 0 deletions .changeset/styled-static-guides.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'@tanstack/charts': minor
---

Add renderer-neutral stroke styling for Cartesian grid rules and axis lines through `grid` and `axis.line` style objects. Authored widths participate in automatic guide margins, and zero-width strokes stay hidden across renderers.
25 changes: 18 additions & 7 deletions API-FRICTION.md
Original file line number Diff line number Diff line change
Expand Up @@ -296,7 +296,7 @@ Each entry records:
| F-257 | The release package graph leaked into application setup | API/Docs/Tooling | resolved |
| F-258 | Tooltip chrome required specificity overrides | API/Documentation | resolved |
| F-259 | Chart resources cannot declare patterns | API | open |
| F-260 | Static guides cannot express stroke treatment | API | open |
| F-260 | Static guides cannot express stroke treatment | API | resolved |
| F-261 | Cartesian bars cannot round only exposed corners | API | resolved |
| F-262 | Mark inference accepted an unsupported style option | API | resolved |
| F-263 | Chromium transport suspension interrupted catalog previews | Tooling | resolved |
Expand Down Expand Up @@ -7957,19 +7957,30 @@ Each entry records:

### F-260 — Static guides cannot express stroke treatment

- Status: open
- Status: resolved
- Severity: medium
- Owner: API
- Observed in: the themed area and active bar dashboard cases 120 and 121
- Friction: `grid` is a boolean, and the static axis line is a boolean. The
theme can set one guide paint, but an author cannot set grid or axis stroke
width, dash, or opacity. F-112 added dashes to rule marks, and F-191 added
tick-label styling. Neither entry covers static axis and grid strokes.
- Current decision: keep the catalog cases on native solid grids with
`theme.grid`. Use rule marks for styled annotations and `crosshair` for
styled focus guides. Do not synthesize repeated grid rules in an application
shell. Keep a guide-style object open for renderer, facet, export, and motion
evaluation.
- Decision: add a renderer-neutral `ChartGuideLineStyle` object to `grid` and
`axis.line` while retaining their boolean forms. Keep shared grid defaults on
the existing group and apply only authored overrides to each scale's rules.
Axis-line styles affect the baseline without changing tick stubs. Use
semantic style comparison for shared facet axis lines so equal objects
created per cell remain compatible while differing baselines are rejected.
Keep each facet cell's grid independent. Include authored stroke extents in
unlocked guide margins, and treat zero-width Canvas strokes as unpainted.
- Verification: focused scene tests preserve byte-equivalent boolean output,
empty-object defaults, per-scale overrides, zero width and opacity, and
baseline-only styling. Thick baseline and grid tests cover automatic surface
containment, including shared outer axes and independent facet grids. Facet
tests cover equal and different baseline values plus per-cell grid styles.
Motion tests cover numeric interpolation and discrete dash and cap updates.
SVG export, Canvas, React Native, and public type-barrel tests preserve the
renderer-neutral style fields, and Canvas skips zero-width strokes.

### F-261 — Cartesian bars cannot round only exposed corners

Expand Down
16 changes: 12 additions & 4 deletions benchmarks/bundle-size/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -25,8 +25,8 @@ may add only its transport module over the tooltip consumer. Ordinary line,
compact-scale, and tooltip kernels also reject all transform modules.

The compact linear scene and React consumer are both locked and budgeted. The
scene has an 11.94 KiB gzip ceiling. The React compact-scale line consumer has a
30.51 KiB ceiling with React and React DOM external. `d3-array` tick helpers are
scene has a 12.46 KiB gzip ceiling. The React compact-scale line consumer has a
31.03 KiB ceiling with React and React DOM external. `d3-array` tick helpers are
allowed only in the compact linear path; categorical compact-scale kernels
reject every D3 runtime input. All compact fixtures reject `d3-scale`,
`d3-format`, `d3-interpolate`, `d3-color`, and `internmap`.
Expand All @@ -41,14 +41,22 @@ Cartesian axis-title styling is also part of the shared scene contract. Its
static SVG fixture adds no retained modules over the ordinary line consumer
and keeps authored title options under a 0.25 KiB incremental gzip ceiling.

Configurable Cartesian grid and axis-line strokes, including geometry-aware
automatic margins, are also part of the default scene contract. Their reviewed
shared-path cost is recorded in the locked entries and complete-consumer
budgets. In the cumulative release, guide styling adds 504 gzip bytes to the
compact scene and 516 bytes to the DOM host. Its complete-consumer ceilings
retain roughly 30 bytes of headroom above the measured output.

Continuous viewports and the controlled interaction controller are also part
of the default scene and host contracts. Default static SVG consumes scene
clips and gradients. Their reviewed shared-path cost is recorded in the locked
entries and the corresponding complete-consumer budgets. Rolling path planning
remains confined to the opt-in motion renderer, whose complete SVG budget is
22.45 KiB gzip. The cumulative styling, focus-motion, and radial-gradient
integration measures 22.42 KiB for this optional renderer and 29.81 KiB for the
composite SVG consumer, whose ceiling is 29.84 KiB. Radial resources add 112
integration measures 22.42 KiB for this optional renderer. The composite SVG
consumer measures 30.33 KiB after guide styling, with a 30.36 KiB ceiling.
Radial resources add 112
gzip bytes to the static SVG consumer and 124 bytes to the DOM host. Scene-only
consumers remain unchanged apart from one byte of compression variation.

Expand Down
40 changes: 20 additions & 20 deletions benchmarks/bundle-size/universal-baseline.json
Original file line number Diff line number Diff line change
Expand Up @@ -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": 51809,
"gzip": 19384
"bytes": 53385,
"gzip": 19890
},
"D3-scale line + static SVG": {
"bytes": 57963,
"gzip": 21674
"bytes": 59539,
"gzip": 22205
},
"Representative marks": {
"bytes": 81705,
"gzip": 29821
"bytes": 83279,
"gzip": 30334
},
"TanStack DOM host": {
"bytes": 80267,
"gzip": 28054
"bytes": 81843,
"gzip": 28570
},
"React adapter": {
"bytes": 82588,
"gzip": 28968
"bytes": 84168,
"gzip": 29511
},
"React line consumer": {
"bytes": 106247,
"gzip": 38313
"bytes": 107827,
"gzip": 38843
},
"Compact-scale line scene": {
"bytes": 34264,
"gzip": 12222
"bytes": 35838,
"gzip": 12726
},
"React compact-scale line consumer": {
"bytes": 88747,
"gzip": 31212
"bytes": 90327,
"gzip": 31742
},
"Custom-scale line scene": {
"bytes": 32446,
"gzip": 11484
"bytes": 34020,
"gzip": 11990
},
"D3 linear-scale line scene": {
"bytes": 51741,
"gzip": 19346
"bytes": 53317,
"gzip": 19852
}
}
}
126 changes: 63 additions & 63 deletions benchmarks/comparison/bundle-baseline.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"schemaVersion": 4,
"generatedAt": "2026-09-10T04:36:47.292Z",
"generatedAt": "2026-09-10T04:42:04.324Z",
"packageVersions": {
"tanstack": "0.17.0",
"chartjs": "4.5.1",
Expand All @@ -11,8 +11,8 @@
"sources": {
"tanstack": {
"kind": "workspace",
"revision": "9e73e9d446c51a613e0781838acf1f718ac30e1b",
"inputDigest": "sha256:af21b1970c5acccf245346d7cac30c7e6e0df58468009de27618b712b514ad0e"
"revision": "87f59846de342cef801f73b84a2965ea0de71631",
"inputDigest": "sha256:0c6de97d4422ab7b195da9f1f3772c8a2fac130fe47ea9b0bb2efcd2380e09cb"
},
"chartjs": {
"kind": "package",
Expand Down Expand Up @@ -45,88 +45,88 @@
},
"bundles": {
"tanstack-line-basic": {
"minifiedBytes": 116338,
"gzipBytes": 42039,
"brotliBytes": 37187,
"incrementalGzipBytes": 42039,
"incrementalBrotliBytes": 37187
"minifiedBytes": 117914,
"gzipBytes": 42548,
"brotliBytes": 37655,
"incrementalGzipBytes": 42548,
"incrementalBrotliBytes": 37655
},
"tanstack-line-interactive": {
"minifiedBytes": 121905,
"gzipBytes": 43868,
"brotliBytes": 38653,
"incrementalGzipBytes": 43868,
"incrementalBrotliBytes": 38653
"minifiedBytes": 123479,
"gzipBytes": 44400,
"brotliBytes": 39090,
"incrementalGzipBytes": 44400,
"incrementalBrotliBytes": 39090
},
"tanstack-line-advanced": {
"minifiedBytes": 129096,
"gzipBytes": 46213,
"brotliBytes": 40690,
"incrementalGzipBytes": 46213,
"incrementalBrotliBytes": 40690
"minifiedBytes": 130673,
"gzipBytes": 46722,
"brotliBytes": 41096,
"incrementalGzipBytes": 46722,
"incrementalBrotliBytes": 41096
},
"tanstack-bar-basic": {
"minifiedBytes": 128778,
"gzipBytes": 46747,
"brotliBytes": 41174,
"incrementalGzipBytes": 46747,
"incrementalBrotliBytes": 41174
"minifiedBytes": 130352,
"gzipBytes": 47280,
"brotliBytes": 41520,
"incrementalGzipBytes": 47280,
"incrementalBrotliBytes": 41520
},
"tanstack-bar-interactive": {
"minifiedBytes": 133200,
"gzipBytes": 48064,
"brotliBytes": 42256,
"incrementalGzipBytes": 48064,
"incrementalBrotliBytes": 42256
"minifiedBytes": 134772,
"gzipBytes": 48610,
"brotliBytes": 42660,
"incrementalGzipBytes": 48610,
"incrementalBrotliBytes": 42660
},
"tanstack-bar-advanced": {
"minifiedBytes": 133539,
"gzipBytes": 48222,
"brotliBytes": 42349,
"incrementalGzipBytes": 48222,
"incrementalBrotliBytes": 42349
"minifiedBytes": 135111,
"gzipBytes": 48751,
"brotliBytes": 42735,
"incrementalGzipBytes": 48751,
"incrementalBrotliBytes": 42735
},
"tanstack-area-basic": {
"minifiedBytes": 121522,
"gzipBytes": 44009,
"brotliBytes": 38924,
"incrementalGzipBytes": 44009,
"incrementalBrotliBytes": 38924
"minifiedBytes": 123094,
"gzipBytes": 44591,
"brotliBytes": 39426,
"incrementalGzipBytes": 44591,
"incrementalBrotliBytes": 39426
},
"tanstack-area-interactive": {
"minifiedBytes": 127093,
"gzipBytes": 45848,
"brotliBytes": 40491,
"incrementalGzipBytes": 45848,
"incrementalBrotliBytes": 40491
"minifiedBytes": 128661,
"gzipBytes": 46371,
"brotliBytes": 40864,
"incrementalGzipBytes": 46371,
"incrementalBrotliBytes": 40864
},
"tanstack-area-advanced": {
"minifiedBytes": 134467,
"gzipBytes": 48217,
"brotliBytes": 42509,
"incrementalGzipBytes": 48217,
"incrementalBrotliBytes": 42509
"minifiedBytes": 136035,
"gzipBytes": 48727,
"brotliBytes": 42967,
"incrementalGzipBytes": 48727,
"incrementalBrotliBytes": 42967
},
"tanstack-scatter-basic": {
"minifiedBytes": 117344,
"gzipBytes": 42407,
"brotliBytes": 37496,
"incrementalGzipBytes": 42407,
"incrementalBrotliBytes": 37496
"minifiedBytes": 118920,
"gzipBytes": 42909,
"brotliBytes": 37959,
"incrementalGzipBytes": 42909,
"incrementalBrotliBytes": 37959
},
"tanstack-scatter-interactive": {
"minifiedBytes": 122911,
"gzipBytes": 44231,
"brotliBytes": 38926,
"incrementalGzipBytes": 44231,
"incrementalBrotliBytes": 38926
"minifiedBytes": 124483,
"gzipBytes": 44749,
"brotliBytes": 39464,
"incrementalGzipBytes": 44749,
"incrementalBrotliBytes": 39464
},
"tanstack-scatter-advanced": {
"minifiedBytes": 122927,
"gzipBytes": 44236,
"brotliBytes": 38951,
"incrementalGzipBytes": 44236,
"incrementalBrotliBytes": 38951
"minifiedBytes": 124499,
"gzipBytes": 44755,
"brotliBytes": 39418,
"incrementalGzipBytes": 44755,
"incrementalBrotliBytes": 39418
},
"chartjs-line-basic": {
"minifiedBytes": 137909,
Expand Down
2 changes: 1 addition & 1 deletion benchmarks/conformance/previews/manifest.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
"schemaVersion": 1,
"width": 288,
"height": 192,
"sourceHash": "01531a4e8c58ab62c022edb1d9ee7116d1ff82fb94cff4a89f307ecb83a2b6b0",
"sourceHash": "e718e368cd7171f8a3d954a3f05716633b0fbbfabd19a2f4fefced1810be1449",
"assets": [
{
"id": "01-line-gaps",
Expand Down
6 changes: 3 additions & 3 deletions docs/comparison.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,14 +12,14 @@ turning untested behavior into a checkmark.

| Library | Package | Measured source |
| -------------------------------------------------------------------------------------- | -------------------- | ------------------- |
| [TanStack Charts](./overview.md) | `@tanstack/charts` | workspace `9e73e9d` |
| [TanStack Charts](./overview.md) | `@tanstack/charts` | workspace `87f5984` |
| [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 `9e73e9d`.
at page render time. The measured TanStack workspace revision is `87f5984`.

## Capability matrix

Expand Down Expand Up @@ -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 | 41.05–47.09 KiB | Not applicable | Controlled suite |
| TanStack Charts | 41.55–47.61 KiB | Not applicable | 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
8 changes: 5 additions & 3 deletions docs/concepts/layout-axes-and-coordinates.md
Original file line number Diff line number Diff line change
Expand Up @@ -167,14 +167,16 @@ const x = {
| Option | Purpose |
| ----------------- | -------------------------------------------------------- |
| `axis` | Configure the axis or hide it with `false` |
| `axis.line` | Show or hide the baseline |
| `axis.line` | Show, hide, or style the baseline |
| `axis.ticks` | Configure candidates, stubs, padding, and formatting |
| `axis.tickLabels` | Configure label rotation and collision thinning |
| `axis.label` | Configure axis title text, typography, paint, and offset |
| `grid` | Draw grid lines at semantic candidates |
| `grid` | Draw or style grid lines at semantic candidates |
| `reverse` | Reverse the responsive range |

The y grid defaults to visible and the x grid defaults to hidden when `grid` is omitted.
Both grids default to hidden when `grid` is omitted. A style object enables
the grid or baseline and accepts renderer-neutral stroke, opacity, width,
dash, and line-cap fields.

Candidate generation and label layout are separate. Choose at most one of
`axis.ticks.count`, `axis.ticks.spacing`, and `axis.ticks.values`. Grid lines
Expand Down
8 changes: 4 additions & 4 deletions docs/guides/bundle-size-and-performance.md
Original file line number Diff line number Diff line change
Expand Up @@ -168,14 +168,14 @@ const interactive = defineChart(definition, {
})
```

The locked compact React line consumer must remain at or below 30.51 KiB gzip.
The locked compact React line consumer must remain at or below 31.03 KiB gzip.
Its retained-module gate rejects tooltip, portal, `d3-scale`, `d3-format`,
`d3-interpolate`, `d3-color`, transforms, and sibling compact-scale entries.
Separate incremental gates limit tooltip and portal growth.

The current locked fixtures measure the compact line scene at 12,222 gzip bytes
versus 19,384 with D3 linear scales. The equivalent React consumers measure
31,212 and 38,313 gzip bytes with React and React DOM external. These are
The current locked fixtures measure the compact line scene at 12,726 gzip bytes
versus 19,890 with D3 linear scales. The equivalent React consumers measure
31,742 and 38,843 gzip bytes with React and React DOM external. These are
fixture measurements, not universal savings claims; they show why the compact
subset is the normal starting point.

Expand Down
Loading