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
The table of contents is too big for display.
Diff view
Diff view
  •  
  •  
  •  
7 changes: 7 additions & 0 deletions .changeset/smooth-shadcn-charts.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
---
'@tanstack/charts': minor
---

Add ShadCN-compatible chart catalog support with renderer-owned tooltip and
entrance motion, composable stagger timing, motion cascade opt-outs, and
expanded polar presentation controls.
226 changes: 226 additions & 0 deletions API-FRICTION.md
Original file line number Diff line number Diff line change
Expand Up @@ -309,6 +309,18 @@ Each entry records:
| F-270 | Catalog migration left generated release evidence stale | Tooling | resolved |
| F-271 | Radial focus collapsed angular cross-sections to centroids | API | resolved |
| F-272 | Pointer probes armed between transient inactive frames | Tooling | resolved |
| F-273 | Catalog cases could not declare an application viewport height | Tooling | resolved |
| F-274 | Upstream example clones had no drift boundary | Tooling | resolved |
| F-275 | Preview transparency validation rejected semantic IDs | Tooling | resolved |
| F-276 | Definition coverage assumed a combined renderer module | Tooling | resolved |
| F-277 | Preview errors omitted the failing catalog case | Tooling | resolved |
| F-278 | Renderer checks could validate matching approximations | Tooling | resolved |
| F-279 | Radial grids could not render authored fills | API | resolved |
| F-280 | Chart motion did not reach HTML tooltips | API | resolved |
| F-281 | Bars could not express an authored outline | API | resolved |
| F-282 | Collection actions followed the viewport instead of the card | Application | resolved |
| F-283 | Interactive chart shells rendered inert controls | Application | resolved |
| F-284 | Stagger timing required repeated callback arithmetic | API | resolved |

## Findings

Expand Down Expand Up @@ -8005,3 +8017,217 @@ Each entry records:
- Verification: the exact Chart.js grouped-pointer cell and the complete quick
partition 3 pass with trusted activation, exact grouped series values, and
zero recovered retries.

### F-273 — Catalog cases could not declare an application viewport height

- Status: resolved
- Severity: low
- Owner: Tooling
- Observed in: reproducing shadcn/ui's dashboard as a renderer-comparison case
- Friction: the catalog fixed every case at 480 pixels tall, which clipped a
full application shell and made its Recharts and TanStack renderers impossible
to inspect as one shared viewport.
- Decision: allow validated case metadata to declare an optional height and use
it for catalog cards, mounts, updates, and embeds while retaining 480 pixels
as the default.
- Verification: metadata tests accept the dashboard's 860-pixel height, the
conformance example build passes, and Chromium renders both comparison panels
at 960 by 860 pixels without root overflow.

### F-274 — Upstream example clones had no drift boundary

- Status: resolved
- Severity: medium
- Owner: Tooling
- Observed in: spiking a maintained TanStack clone of the shadcn chart catalog
- Friction: an example's source URL recorded where it came from, but did not
identify the exact upstream revision or detect changed, added, and removed
examples. Visual clones could silently diverge from shadcn's 70-file catalog.
- Decision: pin the upstream commit, registry blob, and each example's Git blob
SHA in one manifest. Keep normal validation offline and add an explicit remote
drift command that compares the manifest with shadcn's current Git tree.
- Verification: the inventory test requires all 70 files, exact family counts,
unique local mappings, and reports synthetic source changes, additions, and
removals. The offline command verifies all five spike mappings.

### F-275 — Preview transparency validation rejected semantic IDs

- Status: resolved
- Severity: low
- Owner: Tooling
- Observed in: generating the shadcn radial-text catalog preview
- Friction: the preview validator searched the complete SVG string for
`background:`. A valid semantic key such as `radial-background:object`
therefore failed the transparent-background gate.
- Decision: detect an actual CSS `background` declaration after a rule or
declaration boundary, without reserving ordinary chart IDs.
- Verification: the preview regression accepts the semantic background key and
continues to reject an authored CSS background declaration.

### F-276 — Definition-shape coverage assumed a combined renderer module

- Status: resolved
- Severity: low
- Owner: Tooling
- Observed in: isolating TanStack and Recharts bundle graphs for the shadcn
catalog spike
- Friction: the definition-shape gate scanned `tanstack.ts`, `view.tsx`, and
`chart.ts`, but ignored a renderer-specific `tanstack-view.tsx`. Splitting the
two implementations for honest bundle measurement made five valid static
definitions disappear from coverage.
- Decision: include renderer-specific TanStack view modules in the definition
scanner while retaining the existing entry and shared-view conventions.
- Verification: the shape gate finds all 122 definitions again: 117 static and
five responsive.

### F-277 — Preview errors omitted the failing catalog case

- Status: resolved
- Severity: low
- Owner: Tooling
- Observed in: generating static previews for the 70-case shadcn collection
- Friction: the renderer's clipped-label diagnostic described the labels and
bounds but omitted the case ID and theme. A full-catalog run therefore could
not identify which new example needed a margin correction.
- Decision: wrap presentation-validation failures with the current case ID and
theme while retaining the original error as the cause.
- Verification: the next full preview run reports any failed case directly.

### F-278 — Renderer conformance could validate two matching approximations

- Status: resolved
- Severity: high
- Owner: Tooling
- Observed in: expanding the shadcn chart collection from five measured cases
to all 70 upstream examples
- Friction: geometry and paint checks compared TanStack Charts with a local
Recharts implementation built from the same simplified family spec. Both
renderers could agree while their card, data, layout, and variant behavior
visibly differed from the official shadcn output. The later whole-card gate
still allowed a wrong chart type to pass because white card pixels dominated
the score.
- Decision: capture every official shadcn card at a fixed 640-pixel viewport
and compare the complete TanStack card screenshot against that committed
baseline. Require at least 90% pixel similarity and 70% similarity over the
union of non-white pixels in a fixed chart region for every case; retain
failed local and diff images for review.
- Verification: the full gate passes all 70 whole-card and chart-region checks.
Whole-card similarity averages above 97%, and every chart-region score is
above 75%. The earlier wrong radial-stacked implementation would fail at
20.9%. The pinned inventory check also requires one baseline image per
catalog entry and an ordered 70-case reference manifest.

### F-279 — Radial grids could not render authored fills

- Status: resolved
- Severity: medium
- Owner: API
- Observed in: reproducing shadcn's filled polygon and circle radar grids
- Friction: `RadialGridOptions` exposed only stroke styling and the renderer
hard-coded every ring to `fill: none`. Shadcn's filled-grid variants therefore
required a separate approximation instead of expressing their source grid.
- Decision: expose `fill` and `fillOpacity` on `RadialGridOptions` and forward
both values to every generated ring.
- Verification: the polar scene test asserts an authored radial-grid fill and
opacity, and the two filled shadcn radar variants pass the chart-foreground
visual gate above 97%.

### F-280 — Chart motion did not reach HTML tooltips

- Status: resolved
- Severity: medium
- Owner: API
- Observed in: applying the shadcn spring preset to the complete chart catalog
- Friction: chart and mark motion animated renderer geometry, but the HTML
tooltip was owned by a separate extension and appeared, moved, and vanished
immediately. The first tooltip motion pass also restarted every move from the
prior target instead of the spring's live position and velocity, so crossing
points quickly produced visible jumps. Applying one motion policy therefore
produced a visibly split interaction.
- Decision: let the optional `motion()` renderer attach an internal tooltip
motion controller to the host. The tooltip inherits that renderer's fallback
or a static chart-level transition, while `tooltip.motion` can override or
disable it. Keep translation independent from presence motion, and retarget
springs from their current sampled position and velocity. The public tooltip
extension contract stays renderer-neutral.
- Verification: the renderer test asserts that a chart spring produces sampled
tooltip keyframes, keeps the tooltip mounted until its exit completes, and
preserves both visual position and travel direction across a mid-flight
retarget. The tooltip-only bundle retains neither `motion.ts` nor `spring.ts`
and measures 4.37 KiB gzip.

### F-281 — Bars could not express an authored outline

- Status: resolved
- Severity: medium
- Owner: API
- Observed in: reproducing shadcn's active-bar example
- Friction: `barX` and `barY` exposed fill paint but not base stroke paint. The
selected Firefox bar therefore could not reproduce the upstream dashed
outline without replacing the mark or abusing focus-state styling.
- Decision: expose visual `stroke` and `strokeDasharray` channels plus
`strokeOpacity` and `strokeWidth` on both bar orientations.
- Verification: the bar scene test resolves per-datum outlines for horizontal
and vertical bars, and the active-bar screenshot passes at 98% whole-card and
97% chart-region similarity.

### F-282 — Collection actions followed the viewport instead of the card

- Status: resolved
- Severity: medium
- Owner: Application
- Observed in: browsing the two-column ShadCN collection
- Friction: every collection preview reserved the case's fixed conformance
viewport height, while the rendered ShadCN card was often much shorter. The
Code, Preview, and Original actions followed the reserved viewport rather
than the visible card, leaving roughly 280 pixels of empty space and making
the actions appear to belong to the next row.
- Decision: retain the full height as the chart's rendering input, then size
the collection preview shell to the rendered card. Observe the card so the
shell follows responsive height changes without coupling the gallery to
per-case dimensions.
- Verification: the live Radar collection now measures each preview shell to
its visible card and places the action row 14 pixels below it at both columns;
resizing continues to refit through the card observer.

### F-283 — Interactive chart shells rendered inert controls

- Status: resolved
- Severity: high
- Owner: Application
- Observed in: the generated ShadCN area, bar, line, and pie interactive cases
- Friction: the catalog reproduced the controls visually with static `div`
elements, but they had no input semantics or state. The pie month picker,
area range picker, and desktop/mobile metric panels could not change their
charts even though each case was explicitly named interactive.
- Decision: keep interaction state in the shared React example shell and
rebuild the selected chart definition from that state. Use native select and
button semantics, preserve the official labels and active treatment, and
keep the four behaviors centralized rather than adding state code to every
generated case.
- Verification: DOM regressions change the area range, both metric-series
charts, and the active pie month and assert the plotted geometry or paint
changes. Browser checks confirm the 7-day area path and ticks, mobile bar and
line geometry and colors, and May pie slice, swatch, and center value.

### F-284 — Stagger timing required repeated callback arithmetic

- Status: resolved
- Severity: medium
- Owner: API
- Observed in: evaluating optional entrance staggering for stacked radial and
pie compositions
- Friction: every definition had to repeat phase filters, role filters, index
selection, and delay arithmetic. Combining that callback with an existing
transition or rolling-path definition required another handwritten merge.
An initial `composeMotion()` helper recovered composition but made ordinary
timing objects unnecessarily indirect.
- Decision: allow `ChartMotionTiming.delay` to resolve from motion context and
make `stagger()` return that single partial timing field. It composes through
native object spread, whose order also defines conflict precedence. Export it
from `/motion` for convenience and `/motion/definition` as an isolated
policy-only entry.
- Verification: unit tests cover datum and series staggering, phase and role
filters, offsets, invalid inputs, native spread, and field precedence. The
isolated entry is 0.26 KiB gzip and retains neither the SVG motion renderer
nor spring physics.
20 changes: 10 additions & 10 deletions benchmarks/bundle-size/universal-baseline.json
Original file line number Diff line number Diff line change
Expand Up @@ -11,28 +11,28 @@
"gzip": 19616
},
"Representative marks": {
"bytes": 71389,
"gzip": 26201
"bytes": 71536,
"gzip": 26220
},
"TanStack DOM host": {
"bytes": 70570,
"gzip": 24758
"bytes": 70878,
"gzip": 24859
},
"React adapter": {
"bytes": 72729,
"gzip": 25524
"bytes": 73037,
"gzip": 25621
},
"React line consumer": {
"bytes": 95923,
"gzip": 34754
"bytes": 96231,
"gzip": 34868
},
"Compact-scale line scene": {
"bytes": 29572,
"gzip": 10769
},
"React compact-scale line consumer": {
"bytes": 78424,
"gzip": 27684
"bytes": 78732,
"gzip": 27803
},
"Custom-scale line scene": {
"bytes": 27756,
Expand Down
Loading