From 0f35d4cf4bb105bc2c3c50924802f05ed4c3217b Mon Sep 17 00:00:00 2001 From: Tanner Linsley Date: Wed, 12 Aug 2026 16:14:38 -0600 Subject: [PATCH] Add TanStack Charts Intent skills --- .changeset/intent-chart-skills.md | 5 + _artifacts/domain_map.yaml | 862 ++++++++++++++++++ _artifacts/skill_spec.md | 356 ++++++++ _artifacts/skill_tree.yaml | 282 ++++++ benchmarks/conformance/previews/manifest.json | 2 +- packages/charts-core/package.json | 5 + .../skills/build-chart-interactions/SKILL.md | 222 +++++ .../references/interaction-state.md | 53 ++ .../skills/compose-marks-and-views/SKILL.md | 183 ++++ .../references/mark-selection.md | 54 ++ .../configure-scales-guides-color/SKILL.md | 200 ++++ .../references/scale-guide-ownership.md | 62 ++ .../coordinate-charts-with-tanstack/SKILL.md | 210 +++++ .../references/query-db-and-sync.md | 95 ++ .../references/table-and-data-grid.md | 90 ++ .../references/tanstack-application-state.md | 95 ++ .../skills/debug-and-verify-charts/SKILL.md | 163 ++++ .../skills/design-a-chart/SKILL.md | 154 ++++ .../references/visual-task-matrix.md | 54 ++ .../skills/design-responsive-charts/SKILL.md | 206 +++++ .../skills/extend-tanstack-charts/SKILL.md | 158 ++++ .../references/extension-protocols.md | 43 + .../migrate-to-tanstack-charts/SKILL.md | 157 ++++ .../references/chart-js.md | 41 + .../references/d3.md | 50 + .../references/echarts.md | 43 + .../references/observable-plot.md | 43 + .../references/recharts.md | 43 + .../skills/prepare-chart-data/SKILL.md | 214 +++++ .../references/transforms.md | 43 + .../skills/ship-accessible-charts/SKILL.md | 225 +++++ .../references/alpine.md | 22 + .../references/angular.md | 22 + .../ship-accessible-charts/references/lit.md | 22 + .../references/octane.md | 22 + .../references/preact.md | 22 + .../references/react-native.md | 39 + .../references/react.md | 26 + .../references/renderers-and-export.md | 42 + .../references/solid.md | 22 + .../references/svelte.md | 22 + .../ship-accessible-charts/references/vue.md | 22 + .../skills/update-and-animate-charts/SKILL.md | 164 ++++ pnpm-lock.yaml | 27 + 44 files changed, 4886 insertions(+), 1 deletion(-) create mode 100644 .changeset/intent-chart-skills.md create mode 100644 _artifacts/domain_map.yaml create mode 100644 _artifacts/skill_spec.md create mode 100644 _artifacts/skill_tree.yaml create mode 100644 packages/charts-core/skills/build-chart-interactions/SKILL.md create mode 100644 packages/charts-core/skills/build-chart-interactions/references/interaction-state.md create mode 100644 packages/charts-core/skills/compose-marks-and-views/SKILL.md create mode 100644 packages/charts-core/skills/compose-marks-and-views/references/mark-selection.md create mode 100644 packages/charts-core/skills/configure-scales-guides-color/SKILL.md create mode 100644 packages/charts-core/skills/configure-scales-guides-color/references/scale-guide-ownership.md create mode 100644 packages/charts-core/skills/coordinate-charts-with-tanstack/SKILL.md create mode 100644 packages/charts-core/skills/coordinate-charts-with-tanstack/references/query-db-and-sync.md create mode 100644 packages/charts-core/skills/coordinate-charts-with-tanstack/references/table-and-data-grid.md create mode 100644 packages/charts-core/skills/coordinate-charts-with-tanstack/references/tanstack-application-state.md create mode 100644 packages/charts-core/skills/debug-and-verify-charts/SKILL.md create mode 100644 packages/charts-core/skills/design-a-chart/SKILL.md create mode 100644 packages/charts-core/skills/design-a-chart/references/visual-task-matrix.md create mode 100644 packages/charts-core/skills/design-responsive-charts/SKILL.md create mode 100644 packages/charts-core/skills/extend-tanstack-charts/SKILL.md create mode 100644 packages/charts-core/skills/extend-tanstack-charts/references/extension-protocols.md create mode 100644 packages/charts-core/skills/migrate-to-tanstack-charts/SKILL.md create mode 100644 packages/charts-core/skills/migrate-to-tanstack-charts/references/chart-js.md create mode 100644 packages/charts-core/skills/migrate-to-tanstack-charts/references/d3.md create mode 100644 packages/charts-core/skills/migrate-to-tanstack-charts/references/echarts.md create mode 100644 packages/charts-core/skills/migrate-to-tanstack-charts/references/observable-plot.md create mode 100644 packages/charts-core/skills/migrate-to-tanstack-charts/references/recharts.md create mode 100644 packages/charts-core/skills/prepare-chart-data/SKILL.md create mode 100644 packages/charts-core/skills/prepare-chart-data/references/transforms.md create mode 100644 packages/charts-core/skills/ship-accessible-charts/SKILL.md create mode 100644 packages/charts-core/skills/ship-accessible-charts/references/alpine.md create mode 100644 packages/charts-core/skills/ship-accessible-charts/references/angular.md create mode 100644 packages/charts-core/skills/ship-accessible-charts/references/lit.md create mode 100644 packages/charts-core/skills/ship-accessible-charts/references/octane.md create mode 100644 packages/charts-core/skills/ship-accessible-charts/references/preact.md create mode 100644 packages/charts-core/skills/ship-accessible-charts/references/react-native.md create mode 100644 packages/charts-core/skills/ship-accessible-charts/references/react.md create mode 100644 packages/charts-core/skills/ship-accessible-charts/references/renderers-and-export.md create mode 100644 packages/charts-core/skills/ship-accessible-charts/references/solid.md create mode 100644 packages/charts-core/skills/ship-accessible-charts/references/svelte.md create mode 100644 packages/charts-core/skills/ship-accessible-charts/references/vue.md create mode 100644 packages/charts-core/skills/update-and-animate-charts/SKILL.md diff --git a/.changeset/intent-chart-skills.md b/.changeset/intent-chart-skills.md new file mode 100644 index 00000000..61c38328 --- /dev/null +++ b/.changeset/intent-chart-skills.md @@ -0,0 +1,5 @@ +--- +'@tanstack/charts': patch +--- + +Ship scenario-driven TanStack Charts skills for visualization design, responsive behavior, migration, interaction, accessibility, debugging, extension, and coordination with the TanStack data and application ecosystem. diff --git a/_artifacts/domain_map.yaml b/_artifacts/domain_map.yaml new file mode 100644 index 00000000..a0456c6f --- /dev/null +++ b/_artifacts/domain_map.yaml @@ -0,0 +1,862 @@ +# domain_map.yaml +# Generated by TanStack Intent domain discovery +# Library: @tanstack/charts +# Version: 0.9.0 plus the current checkout +# Date: 2026-08-12 +# Status: reviewed + +status: reviewed + +library: + name: '@tanstack/charts' + version: '0.9.0 + current checkout' + repository: 'https://github.com/TanStack/charts' + description: 'A typed, renderer-neutral visualization grammar for responsive application charts.' + primary_framework: 'framework-agnostic' + +discovery: + maintainer_interview: 'skipped after two explicit confirmations' + prioritization_basis: + - 'canonical documentation' + - 'API-FRICTION.md findings F-001 through F-257' + - 'current source assertions and adapter implementations' + - 'CHANGELOG.md migrations' + - 'TanStack/charts GitHub issues' + - 'current official TanStack Table, DB, Query, Router, Store, Virtual, Pacer, Start, and Form documentation' + +authoring_contract: + boundary: >- + Skills add judgment, sequencing, diagnosis, migration strategy, and proof. + They link to canonical documentation for signatures and option inventories + instead of reproducing reference pages. + scenario_schema: + - trigger + - inspect + - decide + - build + - verify + compact_rule: >- + Each scenario should state the decision and its proof in the smallest useful + form. Add code only where the wrong and correct patterns are not obvious. + default_trigger: >- + Load design-a-chart whenever a user asks to graph, chart, visualize, compare, + forecast, project, explain, or communicate a metric. + misleading_request_policy: >- + State the analytical mismatch and recommend a truthful form. If the user + retains the requested form, preserve the data semantics and make the + limitation explicit instead of silently changing the claim. + migration_reference_policy: >- + Generate first-class references for Observable Plot, Recharts, Chart.js, + ECharts, and direct D3. Handle other libraries through the shared semantic + migration workflow until repository evidence justifies another reference. + react_native_policy: >- + Load React Native guidance only when the request or inspected code targets + React Native. Mark device, gesture, accessibility, and performance claims + that still require application-level validation. + +domains: + - name: 'Frame the visualization' + slug: 'frame-the-visualization' + description: 'Turn a user question and metric into an honest visual task with explicit comparison and evidence goals.' + - name: 'Encode application data' + slug: 'encode-application-data' + description: 'Prepare semantic rows and express them through marks, scales, guides, color, and composed views.' + - name: 'Adapt chart behavior' + slug: 'adapt-chart-behavior' + description: 'Make charts respond correctly to containers, interaction, application state, updates, and motion.' + - name: 'Ship and evolve charts' + slug: 'ship-and-evolve-charts' + description: 'Migrate, integrate, extend, test, and deliver charts across renderers and frameworks.' + +skills: + - name: 'Design a chart from a user goal' + slug: 'design-a-chart' + domain: 'frame-the-visualization' + description: 'Choose the comparison, data semantics, visual form, and explanatory evidence before authoring TanStack marks.' + type: 'core' + packages: ['@tanstack/charts'] + covers: + - 'user stories and decisions' + - 'metric semantics, units, denominators, and time grain' + - 'comparison, distribution, relationship, composition, flow, hierarchy, and spatial tasks' + - 'forecast, target, uncertainty, and annotation strategy' + - 'chart acceptance criteria' + tasks: + - 'Graph a product metric for an operational dashboard.' + - 'Explain a KPI change to an executive or customer.' + - 'Choose a visual for actuals, forecasts, targets, and uncertainty.' + reference_candidates: + - topic: 'analytical task and visual-form matrix' + reason: 'Comparison, trend, distribution, relationship, composition, flow, hierarchy, spatial, target, and projection stories need distinct evidence and encoding checks.' + failure_modes: + - mistake: 'Starting with a requested chart type' + mechanism: 'A familiar chart can answer a different question than the user actually needs, producing a polished but irrelevant encoding.' + source: 'docs/guides/choosing-a-chart.md' + priority: 'CRITICAL' + status: 'active' + - mistake: 'Showing a rate without its denominator' + mechanism: 'Percentages and normalized values hide volume, sample size, or the basis of comparison unless the denominator is carried into the story or tooltip.' + source: 'API-FRICTION.md F-217; docs/reference/transforms.md' + priority: 'HIGH' + status: 'active' + - mistake: 'Rendering projections as observed history' + mechanism: 'A continuous visual treatment implies equal certainty; forecast boundaries and uncertainty need an explicit channel, interval, or annotation.' + source: 'docs/examples/lines-and-areas.md; docs/reference/marks/difference.md' + priority: 'CRITICAL' + status: 'active' + - mistake: 'Choosing area or angle for precise ranking' + mechanism: 'Area, radial, and stacked encodings emphasize part-to-whole or shape; aligned position and length are better when exact ordering is the goal.' + source: 'docs/guides/choosing-a-chart.md; docs/examples/bars-and-rankings.md' + priority: 'HIGH' + status: 'active' + + - name: 'Prepare chart data' + slug: 'prepare-chart-data' + domain: 'encode-application-data' + description: 'Shape, aggregate, order, and derive semantic rows while preserving the analytical decision and source lineage.' + type: 'core' + packages: ['@tanstack/charts'] + covers: + - 'mark-local source data' + - 'fold, group, bin, stack, normalize, rank, rolling, cumulative, regression, and hierarchy transforms' + - 'ordering, missing values, derived rows, and lineage' + - 'framework memoization of eager transforms' + tasks: + - 'Turn event rows into daily rates, rolling windows, or ranked categories.' + - 'Prepare histograms, boxplots, stacks, mosaics, or hierarchy inputs.' + - 'Preserve drill-down data through multiple transforms.' + reference_candidates: + - topic: 'transform selection and output contracts' + reason: 'The transform families have distinct grouping, ordering, lineage, empty-value, and interval semantics.' + failure_modes: + - mistake: 'Sorting a line after creating its mark' + mechanism: 'Line and area marks use input order as path order; scale order does not reorder path topology.' + wrong_pattern: | + const mark = lineY(rows, { x: 'date', y: 'value' }) + rows.sort((a, b) => +a.date - +b.date) + correct_pattern: | + const ordered = [...rows].sort((a, b) => +a.date - +b.date) + const mark = lineY(ordered, { x: 'date', y: 'value' }) + source: 'docs/reference/marks/line-and-area.md' + priority: 'CRITICAL' + status: 'active' + - mistake: 'Running eager transforms inside responsive builders' + mechanism: 'Responsive builders may run again for size and layout changes, so uncached data work repeats even when source rows are unchanged.' + wrong_pattern: | + defineChart(({ width }) => ({ + marks: [lineY(rollingWindow(rows, options), channels)], + })) + correct_pattern: | + const prepared = rollingWindow(rows, options) + defineChart(({ width }) => ({ + marks: [lineY(prepared, channels)], + })) + source: 'docs/guides/transforms-and-reactivity.md; API-FRICTION.md F-128' + priority: 'HIGH' + status: 'active' + - mistake: 'Manually accumulating ordinary stacks' + mechanism: 'Case-owned running totals mishandle negative values, missing series, configured order, and later updates even though native stack layouts own those semantics.' + wrong_pattern: | + const start = totals.get(row.x) ?? 0 + return { ...row, y1: start, y2: start + row.value } + correct_pattern: | + barY(rows, { + x: 'x', y: 'value', z: 'series', layout: stack(), + }) + source: 'https://github.com/TanStack/charts/issues/9; API-FRICTION.md F-163' + priority: 'HIGH' + status: 'fixed-but-legacy-risk' + version_context: 'Common in early examples before native stack and group layouts matured.' + - mistake: 'Flattening derived rows without lineage' + mechanism: 'Tooltips, selection, drill-down, and later transforms lose the contributing source records when application preparation drops direct lineage.' + source: 'docs/reference/transforms.md; docs/guides/transforms-and-reactivity.md' + priority: 'HIGH' + status: 'active' + + - name: 'Compose marks and views' + slug: 'compose-marks-and-views' + domain: 'encode-application-data' + description: 'Translate semantic encodings into layered marks, annotations, facets, and coordinated views without duplicating geometry engines.' + type: 'core' + packages: ['@tanstack/charts'] + covers: + - 'mark selection and layer order' + - 'bars, intervals, lines, areas, distributions, spatial, polar, network, and hierarchy compositions' + - 'facets and named view composition' + - 'decorative versus interactive ownership' + tasks: + - 'Layer an area, line, target, annotation, and selected points.' + - 'Build small multiples or a chart with marginal summaries.' + - 'Choose between built-in composites, ordinary marks, and a custom mark.' + reference_candidates: + - topic: 'mark decision matrix' + reason: 'The package exposes many mark families whose important distinction is semantic ownership, not option syntax.' + failure_modes: + - mistake: 'Encoding the whole chart as one custom path' + mechanism: 'A monolithic path discards mark-local data, typed channels, automatic domains, point ownership, renderer portability, and reusable interaction.' + source: 'docs/concepts/grammar-of-graphics.md; docs/guides/custom-marks-and-renderers.md' + priority: 'CRITICAL' + status: 'active' + - mistake: 'Expecting an area to draw its boundary line' + mechanism: 'Area and line are separate layers; relying on an implicit outline loses independent styling, state, and point ownership.' + wrong_pattern: | + marks: [areaY(rows, { x: 'date', y: 'value' })] + correct_pattern: | + marks: [ + areaY(rows, { x: 'date', y: 'value' }), + lineY(rows, { x: 'date', y: 'value' }), + ] + source: 'docs/reference/marks/line-and-area.md' + priority: 'MEDIUM' + status: 'active' + - mistake: 'Letting decorative layers own duplicate points' + mechanism: 'Every interactive child can create another focus and tooltip candidate, so summaries, fills, guides, and labels need deliberate point ownership.' + source: 'API-FRICTION.md F-218; docs/guides/tooltips-and-focus.md' + priority: 'HIGH' + status: 'active' + skills: ['compose-marks-and-views', 'build-chart-interactions'] + - mistake: 'Rebuilding supported coordinate systems in userland' + mechanism: 'Case-owned polar, spatial, hierarchy, and network projection hides responsive final-bounds work and duplicates optional capability modules.' + source: 'API-FRICTION.md F-117, F-199 through F-208; docs/examples/index.md' + priority: 'HIGH' + status: 'fixed-but-legacy-risk' + + - name: 'Configure scales, guides, and color' + slug: 'configure-scales-guides-color' + domain: 'encode-application-data' + description: 'Assign semantic domains, responsive ranges, axes, legends, and paint while keeping scale and grouping ownership explicit.' + type: 'core' + packages: ['@tanstack/charts'] + covers: + - 'compact and D3 scale factories versus configured instances' + - 'domains, responsive ranges, reversal, nicening, and log constraints' + - 'axes, tick collision, margins, legends, and color semantics' + - 'z grouping, color values, and final paint overrides' + tasks: + - 'Choose a numeric, temporal, categorical, log, or color scale.' + - 'Share or lock domains across comparisons.' + - 'Keep labels and legends legible without corrupting chart geometry.' + reference_candidates: + - topic: 'scale and guide ownership matrix' + reason: 'Factories, configured instances, compact scales, D3 families, color kinds, and guide policies form a dense decision surface.' + failure_modes: + - mistake: 'Assigning positional pixel ranges in application code' + mechanism: 'Charts owns final responsive ranges after guide margins resolve; an authored range becomes stale and may invert or misalign geometry.' + wrong_pattern: | + x: { scale: scaleLinear().range([0, 640]) } + correct_pattern: | + x: { scale: scaleLinear } + source: 'API-FRICTION.md F-002; docs/concepts/scales-and-d3.md' + priority: 'CRITICAL' + status: 'active' + skills: ['configure-scales-guides-color', 'design-responsive-charts'] + - mistake: 'Using a default instance for inferred domains' + mechanism: 'A scale instance owns its domain; passing scaleLinear() retains its default domain instead of inferring from mark channels.' + wrong_pattern: | + y: { scale: scaleLinear() } + correct_pattern: | + y: { scale: scaleLinear } + source: 'CHANGELOG.md 0.0.1 breaking migrations; docs/concepts/scales-and-d3.md' + priority: 'CRITICAL' + status: 'active' + - mistake: 'Using color as geometry identity accidentally' + mechanism: 'When z is absent, discrete color may also group paths or bars; a later paint change can therefore alter topology.' + wrong_pattern: | + lineY(rows, { x: 'date', y: 'value', color: 'status' }) + correct_pattern: | + lineY(rows, { + x: 'date', y: 'value', z: 'series', color: 'status', + }) + source: 'API-FRICTION.md F-009 and F-013; docs/concepts/data-and-channels.md' + priority: 'HIGH' + status: 'active' + - mistake: 'Treating containment as collision avoidance' + mechanism: 'Automatic margins keep labels inside the surface; they do not thin, rotate, or prioritize tick labels that overlap one another.' + source: 'API-FRICTION.md F-023 and F-160; docs/guides/responsive-charts.md' + priority: 'HIGH' + status: 'active' + skills: ['configure-scales-guides-color', 'design-responsive-charts'] + + - name: 'Design responsive charts' + slug: 'design-responsive-charts' + domain: 'adapt-chart-behavior' + description: 'Make information, layout, labels, and final-pixel geometry adapt to the chart container and resolved plot.' + type: 'core' + packages: ['@tanstack/charts'] + covers: + - 'container sizing, height, aspect ratio, and initialWidth' + - 'responsive definitions and information priority' + - 'automatic margins and label-density policy' + - 'surface bounds versus final plot bounds' + - 'responsive topology and resolved-layout marks' + tasks: + - 'Make a dashboard chart work from 280 pixels to a wide panel.' + - 'Choose what hides, aggregates, rotates, facets, or becomes a table.' + - 'Author pixel-space layouts that use final inner bounds.' + failure_modes: + - mistake: 'Adapting to viewport width instead of container width' + mechanism: 'Dashboard panels, sidebars, embeds, and nested layouts can be narrow inside a wide viewport; the host measurement is the relevant constraint.' + wrong_pattern: | + const compact = window.innerWidth < 640 + correct_pattern: | + defineChart(({ width }) => ({ + marks, x: { ticks: width < 420 ? 4 : 8 }, + })) + source: 'docs/guides/responsive-charts.md' + priority: 'CRITICAL' + status: 'active' + - mistake: 'Using surface width as final plot width' + mechanism: 'Guides, legends, and automatic margins shrink the final plot; pixel preparation against the surface produces clipped or misaligned marks.' + source: 'API-FRICTION.md F-116 and F-219; docs/guides/responsive-charts.md' + priority: 'CRITICAL' + status: 'active' + - mistake: 'Assuming resize only stretches existing geometry' + mechanism: 'Facets, waffle packing, treemaps, Delaunay links, hexbins, density contours, and label fit can change topology when final bounds change.' + source: 'docs/reference/marks/waffle.md; docs/reference/marks/treemap.md; docs/reference/marks/delaunay.md; docs/reference/marks/hexbin.md' + priority: 'HIGH' + status: 'active' + - mistake: 'Using fixed width for ordinary application charts' + mechanism: 'A fixed width opts out of normal container measurement; omitted initialWidth and flex min-width constraints also cause unstable SSR or overflow.' + wrong_pattern: | + + correct_pattern: | + + source: 'docs/guides/responsive-charts.md; API-FRICTION.md F-111' + priority: 'HIGH' + status: 'active' + + - name: 'Build chart interactions' + slug: 'build-chart-interactions' + domain: 'adapt-chart-behavior' + description: 'Compose focus, tooltips, selection, cursors, brushing, and zoom as semantic controlled behavior with keyboard parity.' + type: 'core' + packages: ['@tanstack/charts'] + covers: + - 'focus strategies and point ownership' + - 'native and composed tooltips, portals, pinning, and anchors' + - 'controlled keyed selection, cursor, brush, and zoom' + - 'pointer and keyboard equivalence' + - 'cross-chart coordination' + tasks: + - 'Add grouped time-series tooltips and a crosshair.' + - 'Coordinate selection between a chart and table.' + - 'Build controlled brush, cursor, or zoom behavior.' + reference_candidates: + - topic: 'interaction state machines' + reason: 'Focus, tooltip, selection, cursor, brush, zoom, pinning, and host ownership each have distinct transition semantics.' + failure_modes: + - mistake: 'Mutating rendered SVG for focus presentation' + mechanism: 'DOM mutation bypasses scene identity, renderer portability, SSR, Canvas, React Native, motion, and cleanup.' + wrong_pattern: | + onRender={({ svg }) => svg.insertBefore(activeBand, svg.firstChild)} + correct_pattern: | + marks: [ + whenFocused(bandX(rows, { x: 'date' }), { match: 'x' }), + lineY(rows, channels), + ] + source: 'https://github.com/TanStack/charts/issues/9; API-FRICTION.md F-178' + priority: 'CRITICAL' + status: 'fixed-but-legacy-risk' + - mistake: 'Focusing a point-less rule mark' + mechanism: 'Rules deliberately emit no interaction points, so a focus filter has no candidate identity to match.' + wrong_pattern: | + whenFocused(ruleX(dates), { match: 'x' }) + correct_pattern: | + focusGuideX(rows, { x: 'date', y: 'value', xRule: {} }) + source: 'https://github.com/TanStack/charts/issues/32; API-FRICTION.md F-237' + priority: 'HIGH' + status: 'fixed-but-legacy-risk' + - mistake: 'Treating controlled callbacks as complete behavior' + mechanism: 'A range or key callback alone does not define gesture capture, keyboard operations, clamping, cancellation, or ownership; use the matching control primitive.' + source: 'API-FRICTION.md F-075; docs/guides/interactions-and-selections.md' + priority: 'CRITICAL' + status: 'active' + - mistake: 'Keeping tooltips inside clipped ancestors' + mechanism: 'Overflow, transforms, and stacking contexts can trap native tooltip geometry even when scene coordinates are correct.' + wrong_pattern: | + tooltip: { use: tooltip } + correct_pattern: | + tooltip: { use: tooltip, portal } + source: 'API-FRICTION.md F-133; docs/guides/tooltips-and-focus.md' + priority: 'HIGH' + status: 'active' + + - name: 'Update and animate charts' + slug: 'update-and-animate-charts' + domain: 'adapt-chart-behavior' + description: 'Connect application state to definition identity, preserve datum identity, and use motion without corrupting responsive or streaming semantics.' + type: 'core' + packages: ['@tanstack/charts'] + covers: + - 'definition identity as the update boundary' + - 'framework memoization and captured values' + - 'stable mark and datum keys' + - 'SVG animation, motion, interruption, and resize policy' + - 'streaming and rolling data' + tasks: + - 'Update a chart when rows or selected metrics change.' + - 'Animate ranking, stack, or focus changes with stable identity.' + - 'Build a streaming or rolling time-series chart.' + failure_modes: + - mistake: 'Creating a fresh definition on every render' + mechanism: 'Definition identity is the runtime update boundary; unnecessary recreation invalidates work and can reset presentation state.' + wrong_pattern: | + + correct_pattern: | + const definition = useMemo(() => makeChart(rows), [rows]) + return + source: 'CHANGELOG.md definition-identity migration; docs/guides/dynamic-data-and-animation.md' + priority: 'CRITICAL' + status: 'active' + - mistake: 'Keying changing entities by row position' + mechanism: 'Reordering, insertion, and deletion then retarget geometry, focus, tooltips, and exit motion to the wrong datum.' + wrong_pattern: | + barX(rows, { x: 'value', y: 'name', key: (_row, i) => i }) + correct_pattern: | + barX(rows, { x: 'value', y: 'name', key: 'id' }) + source: 'API-FRICTION.md F-131 and F-239; docs/guides/dynamic-data-and-animation.md' + priority: 'CRITICAL' + status: 'active' + - mistake: 'Animating every responsive resize' + mechanism: 'Continuous container observation can restart transitions and make layout lag behind the actual panel size.' + wrong_pattern: | + svgAnimation: { resize: true } + correct_pattern: | + svgAnimation: true + source: 'API-FRICTION.md F-129; docs/guides/responsive-charts.md' + priority: 'HIGH' + status: 'active' + - mistake: 'Morphing rolling samples by array index' + mechanism: 'When a window shifts, index identity makes old samples become different times instead of exiting and entering at the edges.' + source: 'API-FRICTION.md F-240; docs/guides/dynamic-data-and-animation.md' + priority: 'HIGH' + status: 'active' + + - name: 'Coordinate charts with TanStack' + slug: 'coordinate-charts-with-tanstack' + domain: 'adapt-chart-behavior' + description: 'Coordinate Charts with TanStack Table, DB, Query, Store, Router, Virtual, Pacer, Start, and Form through explicit data, state, revision, and lifecycle ownership.' + type: 'composition' + packages: + - '@tanstack/charts' + - '@tanstack/table-core' + - '@tanstack/db' + - '@tanstack/query-core' + - '@tanstack/store' + - '@tanstack/router-core' + - '@tanstack/virtual-core' + - '@tanstack/pacer' + covers: + - 'chart and data-grid row projection, filtering, grouping, pagination, and selection' + - 'Query freshness and DB live-query, sync, optimistic, persistence, and rollback state' + - 'semantic intent shared through Store or validated Router search state' + - 'Virtual render windows versus analytical data scope' + - 'Pacer scheduling, Form drafts, and Start SSR boundaries' + tasks: + - 'Build a chart summary and paginated data grid from one semantic projection.' + - 'Connect a live synchronized DB query to chart and grid without duplicating reactive state.' + - 'Coordinate chart selection, URL filters, virtual rows, optimistic edits, and paced updates.' + reference_candidates: + - topic: 'TanStack Table and data-grid coordination' + reason: 'Source, filtered, grouped, sorted, paginated, selected, and virtual row frontiers answer different analytical questions.' + - topic: 'TanStack Query, DB, and sync engines' + reason: 'Freshness, live projections, optimistic state, persistence, rollback, and pressure policies must remain visible to the chart story.' + - topic: 'TanStack application state and lifecycle' + reason: 'Router, Store, Virtual, Pacer, Start, and Form own different state lifetimes that should not be mirrored in chart behavior.' + failure_modes: + - mistake: 'Charting the final rendered row model accidentally' + mechanism: 'The final grid row model can include presentation sorting, grouping, expansion, and pagination that silently changes the chart population.' + source: 'https://tanstack.com/table/latest/docs/guide/row-models' + priority: 'CRITICAL' + status: 'active' + - mistake: 'Keying coordination by row or virtual index' + mechanism: 'Indexes and presentation IDs change under sorting, filtering, grouping, pagination, streaming, and virtualization, retargeting selection and motion.' + source: 'TanStack Table rows guide; API-FRICTION.md F-120, F-131, and F-239' + priority: 'CRITICAL' + status: 'active' + - mistake: 'Duplicating filtering and aggregation' + mechanism: 'Independent grid and chart pipelines drift on eligibility, null policy, denominators, time boundaries, and metric revisions.' + source: 'docs/guides/transforms-and-reactivity.md; API-FRICTION.md F-128 and F-163' + priority: 'CRITICAL' + status: 'active' + - mistake: 'Creating a bidirectional state loop' + mechanism: 'Chart, grid, Store, and Router callbacks echo equivalent updates and create redundant renders, history spam, flicker, or state that cannot settle.' + source: 'docs/guides/interactions-and-selections.md; TanStack Router search-parameter guidance' + priority: 'CRITICAL' + status: 'active' + - mistake: 'Treating optimistic rows as confirmed history' + mechanism: 'A synchronized collection can expose pending values before persistence and later normalize or roll them back, changing what the visual claim means.' + source: 'https://tanstack.com/db/latest/docs/guides/mutations' + priority: 'HIGH' + status: 'active' + - mistake: 'Persisting every interaction frame' + mechanism: 'Pointer-rate writes to Router, Query invalidation, or DB transactions turn disposable presentation into expensive, conflicting, or irreversible state.' + source: 'docs/guides/interactions-and-selections.md; TanStack Pacer timing guidance' + priority: 'CRITICAL' + status: 'active' + - mistake: 'Treating virtualization as analytical filtering' + mechanism: 'Virtual controls which rows are mounted, not which records belong to the metric, unless visible rows are explicitly the question.' + source: 'https://tanstack.com/virtual/latest/docs/api/virtualizer' + priority: 'HIGH' + status: 'active' + + - name: 'Migrate charts to TanStack Charts' + slug: 'migrate-to-tanstack-charts' + domain: 'ship-and-evolve-charts' + description: 'Migrate an existing chart semantically, preserve application-owned behavior, and prove parity before removing the source renderer.' + type: 'composition' + packages: ['@tanstack/charts'] + covers: + - 'semantic inventory and ownership boundaries' + - 'layer-by-layer translation to data, channels, transforms, marks, scales, and controls' + - 'visual, interaction, accessibility, export, bundle, and performance parity' + - 'compatibility package and current subpath migration' + tasks: + - 'Migrate an Observable Plot chart without copying generated DOM.' + - 'Migrate Recharts, Chart.js, ECharts, or a custom D3 renderer.' + - 'Replace a renderer behind a temporary production comparison switch.' + subsystems: + - name: 'Observable Plot' + package: '@observablehq/plot' + config_surface: 'marks, transforms, scales, facets, pointer interactions, and generated SVG' + - name: 'Recharts' + package: 'recharts' + config_surface: 'React component tree, implicit series data, shapes, axes, and tooltip state' + - name: 'Chart.js' + package: 'chart.js' + config_surface: 'dataset configuration, plugins, canvas lifecycle, and imperative updates' + - name: 'ECharts' + package: 'echarts' + config_surface: 'option objects, dataZoom, axisPointer, dispatch actions, and renderer state' + - name: 'D3' + package: 'd3-*' + config_surface: 'application-owned selections, scales, layouts, gestures, and DOM lifecycle' + failure_modes: + - mistake: 'Translating component names one for one' + mechanism: 'Library components combine different data, geometry, scale, and interaction ownership, so name matching preserves syntax rather than behavior.' + source: 'docs/guides/migrating.md' + priority: 'CRITICAL' + status: 'active' + - mistake: 'Replacing transforms and renderer together' + mechanism: 'Changing analytical preparation during a renderer migration makes visual drift impossible to classify.' + source: 'docs/guides/migrating.md; packages/charts-core-d3/docs/tanstack-stats-migration.md' + priority: 'CRITICAL' + status: 'active' + - mistake: 'Calling screenshot similarity complete parity' + mechanism: 'A static image does not prove focus grouping, keyboard behavior, updates, pinning, export, performance, or bundle ownership.' + source: 'API-FRICTION.md F-036, F-073, and F-081; docs/guides/migrating.md' + priority: 'CRITICAL' + status: 'active' + - mistake: 'Reimplementing source-library internals blindly' + mechanism: 'Keep proven D3, SQL, server, or application transforms first; migrate only ownership that TanStack Charts can express and verify.' + source: 'docs/guides/migrating.md; API-FRICTION.md F-127' + priority: 'HIGH' + status: 'active' + compositions: + - library: 'Observable Plot' + skill: 'migrate-to-tanstack-charts' + - library: 'Recharts' + skill: 'migrate-to-tanstack-charts' + - library: 'Chart.js' + skill: 'migrate-to-tanstack-charts' + - library: 'ECharts' + skill: 'migrate-to-tanstack-charts' + - library: 'D3' + skill: 'migrate-to-tanstack-charts' + + - name: 'Ship accessible charts' + slug: 'ship-accessible-charts' + domain: 'ship-and-evolve-charts' + description: 'Deliver deterministic, accessible charts through the correct framework, renderer, SSR, export, and native lifecycle boundaries.' + type: 'lifecycle' + packages: + - '@tanstack/charts' + - '@tanstack/react-charts' + - '@tanstack/react-native-charts' + - '@tanstack/octane-charts' + - '@tanstack/preact-charts' + - '@tanstack/vue-charts' + - '@tanstack/solid-charts' + - '@tanstack/svelte-charts' + - '@tanstack/angular-charts' + - '@tanstack/lit-charts' + - '@tanstack/alpine-charts' + covers: + - 'accessible name, description, keyboard, selection, and adjacent summaries' + - 'SSR, hydration, deterministic IDs, and initial size' + - 'framework adapter mount, update, and cleanup' + - 'SVG, Canvas, React Native SVG, static rendering, and export' + - 'exact package subpaths and optional capability boundaries' + tasks: + - 'Ship a server-rendered chart without hydration drift.' + - 'Provide keyboard and assistive-technology access to important values.' + - 'Choose SVG, Canvas, React Native SVG, or static export.' + subsystems: + - name: 'DOM framework adapters' + package: '@tanstack/charts/* framework subpaths' + config_surface: 'SSR prerender, client mount/update, native tooltip body composition, and cleanup' + - name: 'React Native' + package: '@tanstack/charts/react-native' + config_surface: 'native layout, typography, paint resolution, accessibility actions, and react-native-svg' + - name: 'Renderers and export' + package: '@tanstack/charts' + config_surface: 'SVG, Canvas, renderer-neutral hosts, serialization, and raster export' + reference_candidates: + - topic: 'framework lifecycle deviations' + reason: 'Adapters share the runtime but differ in SSR hooks, portals/slots, custom-element reconnection, and native layout.' + failure_modes: + - mistake: 'Using a generic accessible label' + mechanism: 'A label such as chart or graph does not identify the metric, comparison, period, or unit needed to understand the graphic.' + source: 'docs/guides/accessibility.md' + priority: 'CRITICAL' + status: 'active' + - mistake: 'Making the chart the only critical representation' + mechanism: 'Dense or precise operational values need an adjacent summary, table, or equivalent application interaction.' + source: 'docs/guides/accessibility.md; packages/charts-core-d3/docs/responsive-theme-accessibility.md' + priority: 'HIGH' + status: 'active' + - mistake: 'Mounting the browser host during server rendering' + mechanism: 'Measurement and DOM mutation require real browser elements; server rendering should prerender deterministic SVG and mount only on the client.' + wrong_pattern: | + ngAfterViewInit() { this.adapter.mount(this.surface.nativeElement) } + correct_pattern: | + afterNextRender(() => { + this.adapter.mount(this.surface.nativeElement) + }) + source: 'https://github.com/TanStack/charts/issues/56; docs/guides/ssr-and-hydration.md' + priority: 'CRITICAL' + status: 'fixed-but-legacy-risk' + - mistake: 'Putting chart behavior on adapter props' + mechanism: 'Focus, tooltip, keyboard, cursors, controls, and SVG animation belong to the reusable definition; adapters own surface and callback lifecycle.' + wrong_pattern: | + + correct_pattern: | + const interactive = defineChart(chart, { tooltip, keyboard: true }) + + source: 'CHANGELOG.md chart-behavior migration; docs/framework/react/reference/chart.md' + priority: 'HIGH' + status: 'fixed-but-legacy-risk' + - mistake: 'Importing the universal barrel on native by default' + mechanism: 'Metro can retain browser and optional capability code; exact core and native subpaths protect the native bundle and declaration environment.' + source: 'API-FRICTION.md F-154, F-171, F-173, and F-256; packages/react-native-charts/README.md' + priority: 'HIGH' + status: 'active' + + - name: 'Debug and verify charts' + slug: 'debug-and-verify-charts' + domain: 'ship-and-evolve-charts' + description: 'Diagnose type, geometry, data, interaction, lifecycle, bundle, and performance failures with evidence that matches the user-visible behavior.' + type: 'lifecycle' + packages: ['@tanstack/charts'] + covers: + - 'type-inference diagnosis' + - 'empty, missing, duplicate, negative, and invalid data cases' + - 'scene and rendered geometry inspection' + - 'pointer, keyboard, update, and teardown scenarios' + - 'packed-package, bundle, and performance verification' + tasks: + - 'Fix a type error without erasing chart inference.' + - 'Explain why a chart is blank, clipped, misleading, or hard to focus.' + - 'Prove a migration or optimization with executable scenarios.' + failure_modes: + - mistake: 'Casting a rejected chart definition' + mechanism: 'A cast hides an inaccurate row type, incompatible channel, wrong scale domain, mixed union, or custom mark contract and leaves the runtime mismatch intact.' + wrong_pattern: | + + correct_pattern: | + // Correct the row, channel, scale, or custom mark type at its source. + + source: 'docs/guides/typescript.md; docs/reference/types.md' + priority: 'CRITICAL' + status: 'active' + - mistake: 'Asserting only that an element exists' + mechanism: 'Presence does not prove correct geometry, containment, tooltip state, focused datum, keyboard order, or update behavior.' + source: 'API-FRICTION.md F-036, F-073, and F-081; docs/guides/testing-and-debugging.md' + priority: 'CRITICAL' + status: 'active' + - mistake: 'Testing workspace source instead of packed exports' + mechanism: 'Workspace resolution can hide missing exports, browser globals, peer requirements, and retained optional dependencies that fail for consumers.' + source: 'API-FRICTION.md F-090, F-139, F-167, and F-224' + priority: 'HIGH' + status: 'active' + - mistake: 'Trusting one aggregate performance number' + mechanism: 'Representation cost, sustained pointer work, update correctness, and dashboard lifecycle can disappear inside one mount median or one-shot probe.' + source: 'API-FRICTION.md F-078 through F-084; docs/guides/bundle-size-and-performance.md' + priority: 'HIGH' + status: 'active' + + - name: 'Extend TanStack Charts' + slug: 'extend-tanstack-charts' + domain: 'ship-and-evolve-charts' + description: 'Implement custom marks, layouts, renderers, controls, and composed views only where built-in primitives cannot express the required semantics.' + type: 'core' + packages: ['@tanstack/charts'] + covers: + - 'createMark and scale-value variants' + - 'renderer-neutral scene nodes and interaction points' + - 'resolved-layout marks and final-pixel work' + - 'custom renderers and host extensions' + - 'composable view ownership boundaries' + tasks: + - 'Build a bespoke Cartesian mark that retains native scales and focus.' + - 'Add a final-bounds layout without DOM access or side effects.' + - 'Implement a renderer or host extension with typed scene callbacks.' + reference_candidates: + - topic: 'extension protocols' + reason: 'Marks, resolved layouts, scenes, renderers, host extensions, controls, and composable views are separate ownership boundaries.' + failure_modes: + - mistake: 'Reading or mutating the DOM during scene generation' + mechanism: 'Scene compilation must remain deterministic and renderer-neutral for SSR, Canvas, React Native, export, and testing.' + source: 'docs/guides/custom-marks-and-renderers.md; packages/charts-core/src/mark.ts' + priority: 'CRITICAL' + status: 'active' + - mistake: 'Inferring a private positional domain during render' + mechanism: 'The chart cannot coordinate guides, layers, focus, or linked views unless a mark declares materialized scale channels before rendering.' + source: 'docs/reference/custom-extensions.md; packages/charts-core-d3/docs/custom-marks.md' + priority: 'CRITICAL' + status: 'active' + - mistake: 'Conflating interaction and scale values' + mechanism: 'Interval and custom layouts may focus one semantic value while materializing several endpoints; createMark alone can make axis requirements and callbacks dishonest.' + wrong_pattern: | + createMark(initialize) + correct_pattern: | + createMarkWithScaleValues( + initialize, + ) + source: 'API-FRICTION.md F-094; docs/reference/types.md' + priority: 'HIGH' + status: 'active' + - mistake: 'Running side effects in resolved-layout callbacks' + mechanism: 'Margin solving and responsive layout may evaluate callbacks more than once; reducers and child builders must be pure, synchronous, and deterministic.' + source: 'docs/reference/marks/hexbin.md; docs/reference/marks/sankey.md' + priority: 'HIGH' + status: 'active' + +tensions: + - name: 'Analytical honesty versus visual simplicity' + skills: ['design-a-chart', 'prepare-chart-data'] + description: 'Aggregation, normalization, projection, and missing-value policy can simplify a chart while hiding the evidence needed to interpret it.' + implication: 'An agent optimizes appearance before preserving denominators, lineage, uncertainty, and comparison validity.' + - name: 'Responsive adaptation versus comparison stability' + skills: ['design-responsive-charts', 'configure-scales-guides-color'] + description: 'Narrow layouts need fewer labels and different composition, but changing domains or semantics breaks comparison across sizes.' + implication: 'An agent makes the chart fit by silently changing what positions or colors mean.' + - name: 'Rich interaction versus portable rendering' + skills: + [ + 'build-chart-interactions', + 'ship-accessible-charts', + 'extend-tanstack-charts', + ] + description: 'DOM conveniences can produce rich local UI while breaking SSR, Canvas, React Native, export, or keyboard equivalence.' + implication: 'An agent reaches for DOM mutation before renderer-neutral marks, controls, or host extensions.' + - name: 'Motion continuity versus current-state correctness' + skills: + [ + 'update-and-animate-charts', + 'design-responsive-charts', + 'debug-and-verify-charts', + ] + description: 'Long or interrupted transitions can preserve visual continuity while presenting stale geometry after updates or resize.' + implication: 'An agent verifies only final frames and misses stale focus, guides, or latest-wins behavior during interruption.' + - name: 'Local responsiveness versus synchronized truth' + skills: + [ + 'coordinate-charts-with-tanstack', + 'build-chart-interactions', + 'update-and-animate-charts', + ] + description: 'Immediate local feedback can outrun shared filters, server revisions, optimistic persistence, or rollback.' + implication: 'An agent makes the chart feel responsive while chart, grid, URL, and synchronized data no longer describe the same accepted state.' + +cross_references: + - from: 'design-a-chart' + to: ['prepare-chart-data', 'compose-marks-and-views'] + reason: 'The chosen analytical task determines both the transform and the mark composition.' + - from: 'prepare-chart-data' + to: ['configure-scales-guides-color', 'build-chart-interactions'] + reason: 'Derived values determine domains, legends, tooltip content, and available lineage.' + - from: 'compose-marks-and-views' + to: ['build-chart-interactions', 'extend-tanstack-charts'] + reason: 'Layering determines point ownership, and built-in composition should be exhausted before extending the grammar.' + - from: 'configure-scales-guides-color' + to: ['design-responsive-charts', 'debug-and-verify-charts'] + reason: 'Final ranges and guide layout depend on size, and many blank or clipped charts are scale-contract failures.' + - from: 'design-responsive-charts' + to: ['ship-accessible-charts', 'update-and-animate-charts'] + reason: 'Initial SSR size, label priority, and resize policy affect hydration, accessibility, and motion.' + - from: 'build-chart-interactions' + to: + [ + 'coordinate-charts-with-tanstack', + 'ship-accessible-charts', + 'update-and-animate-charts', + ] + reason: 'Interaction requires explicit cross-surface ownership, keyboard parity, and stable identity across updates.' + - from: 'coordinate-charts-with-tanstack' + to: + [ + 'prepare-chart-data', + 'build-chart-interactions', + 'update-and-animate-charts', + 'debug-and-verify-charts', + ] + reason: 'Ecosystem coordination depends on one semantic projection, one intent owner, stable update identity, and end-to-end synchronization evidence.' + - from: 'migrate-to-tanstack-charts' + to: ['design-a-chart', 'debug-and-verify-charts'] + reason: 'Migration must restate the analytical task and prove parity beyond screenshots.' + - from: 'ship-accessible-charts' + to: ['debug-and-verify-charts'] + reason: 'SSR, accessibility, renderer, and package-boundary claims need consumer-level verification.' + - from: 'extend-tanstack-charts' + to: ['compose-marks-and-views', 'debug-and-verify-charts'] + reason: 'Extensions should be justified against native composition and verified across renderer boundaries.' + +gaps: + - skill: 'design-a-chart' + question: 'How opinionated should skills be about refusing misleading user-requested chart forms versus implementing them with warnings?' + context: 'The documentation describes task fit but does not define the product policy for agent pushback.' + status: 'resolved' + resolution: 'Recommend the truthful form first; preserve an insisted form only with explicit analytical limitations.' + - skill: 'migrate-to-tanstack-charts' + question: 'Which source libraries beyond Observable Plot, Recharts, Chart.js, ECharts, and direct D3 deserve first-class migration references?' + context: 'Repository evidence is strongest for those five ecosystems.' + status: 'resolved' + resolution: 'Generate the five evidence-backed references and route other libraries through the shared semantic workflow.' + - skill: 'ship-accessible-charts' + question: 'When should experimental React Native guidance be generated versus withheld until the user explicitly requests it?' + context: 'The adapter is published but its own README still marks broad device, gesture, accessibility, and performance validation as incomplete.' + status: 'resolved' + resolution: 'Load native guidance only for an explicit React Native target and retain application-level validation caveats.' + +issue_evidence: + - cluster: 'Tooltip, focus, and responsive presentation' + references: + - 'https://github.com/TanStack/charts/issues/9' + - 'https://github.com/TanStack/charts/issues/32' + - 'https://github.com/TanStack/charts/issues/33' + finding: 'Developer workarounds mutated SVG or composed point-less focus marks before renderer-neutral focus and guide primitives were available.' + - cluster: 'Scale typing and ownership' + references: ['https://github.com/TanStack/charts/issues/34'] + finding: 'Configured D3 scales and heterogeneous mark unions produced casts even though runtime ownership was correct.' + - cluster: 'SSR adapter lifecycle' + references: ['https://github.com/TanStack/charts/issues/56'] + finding: 'Mounting the DOM host from an Angular server lifecycle called measurement and mutation on server placeholders.' + - cluster: 'Motion correctness' + references: ['https://github.com/TanStack/charts/issues/71'] + finding: 'Generic numeric path interpolation treated arc flags as continuous values and produced invalid intermediate geometry.' + - cluster: 'Open product boundaries' + references: + - 'https://github.com/TanStack/charts/issues/28' + - 'https://github.com/TanStack/charts/issues/69' + - 'https://github.com/TanStack/charts/issues/87' + finding: 'Selective rectangle corners, modifier-gated zoom, and logical axis placement remain active constraints that skills must not invent.' + +documentation_read: + canonical_docs: 'Every Markdown file under docs/, including concepts, examples, framework guides, guides, references, and all mark references.' + generated_docs: 'packages/charts-core/docs is an exact generated copy of docs/ and was deduplicated after verification.' + archived_docs: 'Every Markdown file under packages/charts-core-d3/docs was read as historical migration and failure-mode evidence.' + package_docs: 'Root README plus primary @tanstack/charts, compact-scale, React, React Native, and representative adapter package READMEs and implementations.' + project_evidence: 'API-FRICTION.md F-001 through F-257 and relevant CHANGELOG.md migration sections.' + community: 'All ten public GitHub issues found in the current repository were reviewed; three remain open.' diff --git a/_artifacts/skill_spec.md b/_artifacts/skill_spec.md new file mode 100644 index 00000000..b67f43e9 --- /dev/null +++ b/_artifacts/skill_spec.md @@ -0,0 +1,356 @@ +# TanStack Charts — Skill Spec + +TanStack Charts is a typed, renderer-neutral visualization grammar for application charts. These skills teach agents how to make visualization decisions, compose the grammar, migrate existing charts, and verify behavior without copying the API reference into skill bodies. + +Status: reviewed. The maintainer directed generation from repository evidence without further interview rounds. + +## Authoring Contract + +Every scenario uses the same compact sequence: + +1. **Trigger** — state the user request or failure that loads the scenario. +2. **Inspect** — identify the data semantics, existing ownership, environment, and constraints. +3. **Decide** — choose the comparison, transform, mark, scale, behavior, or migration boundary. +4. **Build** — show only the non-obvious TanStack Charts composition. +5. **Verify** — name the evidence that proves the result. + +Canonical documentation owns signatures and option inventories. Skills own judgment, sequencing, failure modes, migration strategy, and verification. + +## Domains + +| Domain | Description | Skills | +| ----------------------- | ------------------------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------- | +| Frame the visualization | Turn a user question and metric into an honest visual task with explicit comparison and evidence goals. | `design-a-chart` | +| Encode application data | Prepare semantic rows and express them through marks, scales, guides, color, and composed views. | `prepare-chart-data`, `compose-marks-and-views`, `configure-scales-guides-color` | +| Adapt chart behavior | Make charts respond correctly to containers, interaction, application state, updates, motion, and surrounding TanStack systems. | `design-responsive-charts`, `build-chart-interactions`, `update-and-animate-charts`, `coordinate-charts-with-tanstack` | +| Ship and evolve charts | Migrate, integrate, extend, test, and deliver charts across renderers and frameworks. | `migrate-to-tanstack-charts`, `ship-accessible-charts`, `debug-and-verify-charts`, `extend-tanstack-charts` | + +## Skill Inventory + +| Skill | Type | Domain | What it covers | Failure modes | +| --------------------------------- | ----------- | ----------------------- | -------------------------------------------------------------------------------------- | ------------: | +| `design-a-chart` | core | frame-the-visualization | User goals, metric semantics, task-to-visual mapping, projections, acceptance criteria | 4 | +| `prepare-chart-data` | core | encode-application-data | Ordering, aggregation, transforms, missing values, derived rows, lineage | 4 | +| `compose-marks-and-views` | core | encode-application-data | Mark choice, layering, annotations, facets, coordinated views, point ownership | 4 | +| `configure-scales-guides-color` | core | encode-application-data | Domains, ranges, axes, legends, label policy, grouping, paint | 4 | +| `design-responsive-charts` | core | adapt-chart-behavior | Container sizing, information priority, margins, final bounds, topology | 4 | +| `build-chart-interactions` | core | adapt-chart-behavior | Focus, tooltips, selection, cursors, brush, zoom, keyboard parity | 4 | +| `update-and-animate-charts` | core | adapt-chart-behavior | Definition identity, keys, animation, interruption, streaming | 4 | +| `coordinate-charts-with-tanstack` | composition | adapt-chart-behavior | Table row scopes, Query/DB sync, shared state, virtualization, pacing, SSR | 7 | +| `migrate-to-tanstack-charts` | composition | ship-and-evolve-charts | Semantic inventory, ownership translation, phased migration, parity | 4 | +| `ship-accessible-charts` | lifecycle | ship-and-evolve-charts | Accessibility, SSR, adapters, renderers, export, package boundaries | 5 | +| `debug-and-verify-charts` | lifecycle | ship-and-evolve-charts | Types, data, geometry, interactions, packed consumers, performance | 4 | +| `extend-tanstack-charts` | core | ship-and-evolve-charts | Custom marks, resolved layouts, renderers, hosts, controls, views | 4 | + +## Failure Mode Inventory + +### Design a chart from a user goal (4) + +| # | Mistake | Priority | Source | Cross-skill? | +| --: | ------------------------------------------ | -------- | --------------------------------------------------- | ------------ | +| 1 | Starting with a requested chart type | CRITICAL | `docs/guides/choosing-a-chart.md` | — | +| 2 | Showing a rate without its denominator | HIGH | `API-FRICTION.md` F-217; transforms reference | — | +| 3 | Rendering projections as observed history | CRITICAL | Lines and areas examples; difference mark reference | — | +| 4 | Choosing area or angle for precise ranking | HIGH | Chart-choice guide; bar and ranking examples | — | + +### Prepare chart data (4) + +| # | Mistake | Priority | Source | Cross-skill? | +| --: | --------------------------------------------------- | -------- | --------------------------------------------------- | ------------ | +| 1 | Sorting a line after creating its mark | CRITICAL | Line and area reference | — | +| 2 | Running eager transforms inside responsive builders | HIGH | Transform/reactivity guide; `API-FRICTION.md` F-128 | — | +| 3 | Manually accumulating ordinary stacks | HIGH | GitHub issue 9; `API-FRICTION.md` F-163 | — | +| 4 | Flattening derived rows without lineage | HIGH | Transforms reference and guide | — | + +### Compose marks and views (4) + +| # | Mistake | Priority | Source | Cross-skill? | +| --: | --------------------------------------------------- | -------- | ---------------------------------------------- | -------------------------- | +| 1 | Encoding the whole chart as one custom path | CRITICAL | Grammar and custom-mark guides | — | +| 2 | Expecting an area to draw its boundary line | MEDIUM | Line and area reference | — | +| 3 | Letting decorative layers own duplicate points | HIGH | `API-FRICTION.md` F-218; tooltip/focus guide | `build-chart-interactions` | +| 4 | Rebuilding supported coordinate systems in userland | HIGH | `API-FRICTION.md` F-117, F-199–F-208; examples | — | + +### Configure scales, guides, and color (4) + +| # | Mistake | Priority | Source | Cross-skill? | +| --: | ----------------------------------------------------- | -------- | ------------------------------------------------ | -------------------------- | +| 1 | Assigning positional pixel ranges in application code | CRITICAL | `API-FRICTION.md` F-002; scales guide | `design-responsive-charts` | +| 2 | Using a default instance for inferred domains | CRITICAL | 0.0.1 changelog migration; scales guide | — | +| 3 | Using color as geometry identity accidentally | HIGH | `API-FRICTION.md` F-009, F-013; channels concept | — | +| 4 | Treating containment as collision avoidance | HIGH | `API-FRICTION.md` F-023, F-160; responsive guide | `design-responsive-charts` | + +### Design responsive charts (4) + +| # | Mistake | Priority | Source | Cross-skill? | +| --: | ----------------------------------------------------- | -------- | ------------------------------------------------ | ------------ | +| 1 | Adapting to viewport width instead of container width | CRITICAL | Responsive guide | — | +| 2 | Using surface width as final plot width | CRITICAL | `API-FRICTION.md` F-116, F-219; responsive guide | — | +| 3 | Assuming resize only stretches existing geometry | HIGH | Waffle, treemap, Delaunay, and hexbin references | — | +| 4 | Using fixed width for ordinary application charts | HIGH | Responsive guide; `API-FRICTION.md` F-111 | — | + +### Build chart interactions (4) + +| # | Mistake | Priority | Source | Cross-skill? | +| --: | -------------------------------------------------- | -------- | -------------------------------------------- | ------------ | +| 1 | Mutating rendered SVG for focus presentation | CRITICAL | GitHub issue 9; `API-FRICTION.md` F-178 | — | +| 2 | Focusing a point-less rule mark | HIGH | GitHub issue 32; `API-FRICTION.md` F-237 | — | +| 3 | Treating controlled callbacks as complete behavior | CRITICAL | `API-FRICTION.md` F-075; interactions guide | — | +| 4 | Keeping tooltips inside clipped ancestors | HIGH | `API-FRICTION.md` F-133; tooltip/focus guide | — | + +### Update and animate charts (4) + +| # | Mistake | Priority | Source | Cross-skill? | +| --: | ------------------------------------------- | -------- | -------------------------------------------------- | ------------ | +| 1 | Creating a fresh definition on every render | CRITICAL | Definition-identity migration; dynamic-data guide | — | +| 2 | Keying changing entities by row position | CRITICAL | `API-FRICTION.md` F-131, F-239; dynamic-data guide | — | +| 3 | Animating every responsive resize | HIGH | `API-FRICTION.md` F-129; responsive guide | — | +| 4 | Morphing rolling samples by array index | HIGH | `API-FRICTION.md` F-240; dynamic-data guide | — | + +### Coordinate charts with TanStack (7) + +| # | Mistake | Priority | Source | Cross-skill? | +| --: | -------------------------------------------------- | -------- | ------------------------------------------------- | --------------------------- | +| 1 | Charting the final rendered row model accidentally | CRITICAL | TanStack Table row-model guide | `prepare-chart-data` | +| 2 | Keying coordination by row or virtual index | CRITICAL | Table rows; `API-FRICTION.md` F-120, F-131, F-239 | `update-and-animate-charts` | +| 3 | Duplicating filtering and aggregation | CRITICAL | Transform guide; `API-FRICTION.md` F-128, F-163 | `prepare-chart-data` | +| 4 | Creating a bidirectional state loop | CRITICAL | Chart interactions; Router search state | `build-chart-interactions` | +| 5 | Treating optimistic rows as confirmed history | HIGH | TanStack DB mutations and live queries | `update-and-animate-charts` | +| 6 | Persisting every interaction frame | CRITICAL | Chart interactions; TanStack Pacer timing | `build-chart-interactions` | +| 7 | Treating virtualization as analytical filtering | HIGH | TanStack Virtual virtualizer | `design-responsive-charts` | + +### Migrate charts to TanStack Charts (4) + +| # | Mistake | Priority | Source | Cross-skill? | +| --: | ----------------------------------------------- | -------- | ------------------------------------------------------ | ------------ | +| 1 | Translating component names one for one | CRITICAL | Migration guide | — | +| 2 | Replacing transforms and renderer together | CRITICAL | Migration guide; TanStack Stats migration notes | — | +| 3 | Calling screenshot similarity complete parity | CRITICAL | `API-FRICTION.md` F-036, F-073, F-081; migration guide | — | +| 4 | Reimplementing source-library internals blindly | HIGH | Migration guide; `API-FRICTION.md` F-127 | — | + +### Ship accessible charts (5) + +| # | Mistake | Priority | Source | Cross-skill? | +| --: | --------------------------------------------------- | -------- | ---------------------------------------------------------------- | ------------ | +| 1 | Using a generic accessible label | CRITICAL | Accessibility guide | — | +| 2 | Making the chart the only critical representation | HIGH | Accessibility guide; archived accessibility notes | — | +| 3 | Mounting the browser host during server rendering | CRITICAL | GitHub issue 56; SSR guide | — | +| 4 | Putting chart behavior on adapter props | HIGH | Chart-behavior migration; React adapter reference | — | +| 5 | Importing the universal barrel on native by default | HIGH | `API-FRICTION.md` F-154, F-171, F-173, F-256; native quick start | — | + +### Debug and verify charts (4) + +| # | Mistake | Priority | Source | Cross-skill? | +| --: | -------------------------------------------------- | -------- | ---------------------------------------------------- | ------------ | +| 1 | Casting a rejected chart definition | CRITICAL | TypeScript guide and types reference | — | +| 2 | Asserting only that an element exists | CRITICAL | `API-FRICTION.md` F-036, F-073, F-081; testing guide | — | +| 3 | Testing workspace source instead of packed exports | HIGH | `API-FRICTION.md` F-090, F-139, F-167, F-224 | — | +| 4 | Trusting one aggregate performance number | HIGH | `API-FRICTION.md` F-078–F-084; performance guide | — | + +### Extend TanStack Charts (4) + +| # | Mistake | Priority | Source | Cross-skill? | +| --: | --------------------------------------------------- | -------- | ------------------------------------------------------ | ------------ | +| 1 | Reading or mutating the DOM during scene generation | CRITICAL | Custom-mark guide; `packages/charts-core/src/mark.ts` | — | +| 2 | Inferring a private positional domain during render | CRITICAL | Custom-extension reference; archived custom-mark notes | — | +| 3 | Conflating interaction and scale values | HIGH | `API-FRICTION.md` F-094; types reference | — | +| 4 | Running side effects in resolved-layout callbacks | HIGH | Hexbin and Sankey references | — | + +## Tensions + +| Tension | Skills | Agent implication | +| -------------------------------------------------- | -------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------ | +| Analytical honesty versus visual simplicity | `design-a-chart` ↔ `prepare-chart-data` | Appearance wins before denominators, lineage, uncertainty, or comparison validity are preserved. | +| Responsive adaptation versus comparison stability | `design-responsive-charts` ↔ `configure-scales-guides-color` | A chart is made to fit by silently changing what position or color means. | +| Rich interaction versus portable rendering | `build-chart-interactions` ↔ `ship-accessible-charts` ↔ `extend-tanstack-charts` | DOM mutation is chosen before renderer-neutral marks, controls, or hosts. | +| Motion continuity versus current-state correctness | `update-and-animate-charts` ↔ `design-responsive-charts` ↔ `debug-and-verify-charts` | Final frames pass while interruption leaves stale focus, guides, or geometry. | +| Local responsiveness versus synchronized truth | `coordinate-charts-with-tanstack` ↔ `build-chart-interactions` ↔ `update-and-animate-charts` | Chart, grid, URL, and synchronized data can describe different accepted states. | + +## Cross-References + +| From | To | Reason | +| --------------------------------- | -------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------- | +| `design-a-chart` | `prepare-chart-data`, `compose-marks-and-views` | The analytical task determines the transform and mark composition. | +| `prepare-chart-data` | `configure-scales-guides-color`, `build-chart-interactions` | Derived values determine domains, legends, tooltip content, and lineage. | +| `compose-marks-and-views` | `build-chart-interactions`, `extend-tanstack-charts` | Layering determines point ownership; native composition should precede extension. | +| `configure-scales-guides-color` | `design-responsive-charts`, `debug-and-verify-charts` | Final ranges depend on size, and blank charts often expose scale-contract failures. | +| `design-responsive-charts` | `ship-accessible-charts`, `update-and-animate-charts` | Initial size and resize policy affect hydration, accessibility, and motion. | +| `build-chart-interactions` | `coordinate-charts-with-tanstack`, `ship-accessible-charts`, `update-and-animate-charts` | Interactions require explicit cross-surface ownership, keyboard parity, and stable update identity. | +| `coordinate-charts-with-tanstack` | `prepare-chart-data`, `build-chart-interactions`, `update-and-animate-charts`, `debug-and-verify-charts` | Ecosystem coordination needs one semantic projection, intent owner, identity contract, and synchronization proof. | +| `migrate-to-tanstack-charts` | `design-a-chart`, `debug-and-verify-charts` | Migration must restate the analytical task and prove parity beyond screenshots. | +| `ship-accessible-charts` | `debug-and-verify-charts` | SSR, accessibility, renderer, and package claims need consumer-level proof. | +| `extend-tanstack-charts` | `compose-marks-and-views`, `debug-and-verify-charts` | Extensions need justification against native composition and cross-renderer proof. | + +## Subsystems & Reference Candidates + +| Skill | Subsystems | Reference candidates | +| --------------------------------- | -------------------------------------------------------------------------------- | ------------------------------------------- | +| `prepare-chart-data` | — | Transform selection and output contracts | +| `compose-marks-and-views` | — | Mark decision matrix | +| `configure-scales-guides-color` | — | Scale and guide ownership matrix | +| `build-chart-interactions` | — | Interaction state machines | +| `coordinate-charts-with-tanstack` | Table and grids; Query, DB, and sync; Router, Store, Virtual, Pacer, Start, Form | Cross-library ownership and synchronization | +| `migrate-to-tanstack-charts` | Observable Plot, Recharts, Chart.js, ECharts, D3 | Per-library ownership and parity mappings | +| `ship-accessible-charts` | DOM adapters, React Native, renderers and export | Framework lifecycle deviations | +| `extend-tanstack-charts` | — | Extension protocols | + +## Resolved Policies + +| Skill | Policy | Status | +| ---------------------------- | ------------------------------------------------------------------------------------------------------- | -------- | +| `design-a-chart` | Recommend a truthful form first; preserve an insisted form only with explicit analytical limitations. | resolved | +| `migrate-to-tanstack-charts` | Generate five evidence-backed source-library references; use the shared semantic workflow for others. | resolved | +| `ship-accessible-charts` | Load React Native guidance only for an explicit native target and require application-level validation. | resolved | + +## Recommended Skill File Structure + +- **Core skills:** eight framework-neutral entry points cover chart design, data preparation, composition, scales, responsiveness, interaction, updates, and extension. +- **Framework guidance:** lifecycle deviations live under `ship-accessible-charts/references/`, split into one reference per adapter. +- **Lifecycle skills:** `ship-accessible-charts` and `debug-and-verify-charts` cover production delivery and evidence. +- **Composition skills:** `migrate-to-tanstack-charts` owns source-library mappings; `coordinate-charts-with-tanstack` owns cross-library data and state boundaries. +- **Reference files:** use references for transforms, mark selection, scales/guides, interactions, migrations, adapter lifecycle, and extension protocols. Keep each `SKILL.md` on the scenario contract and highest-risk failures. + +## Composition Opportunities + +| Library | Integration points | Composition skill needed? | +| --------------------------------------------------------------- | ---------------------------------------------------------------------------- | ------------------------------------------------------------------------ | +| Observable Plot | Marks, transforms, scales, facets, pointer behavior, generated SVG | Yes — `migrate-to-tanstack-charts` reference | +| Recharts | React components, implicit series data, shapes, axes, tooltip state | Yes — `migrate-to-tanstack-charts` reference | +| Chart.js | Dataset configuration, plugins, Canvas lifecycle, imperative updates | Yes — `migrate-to-tanstack-charts` reference | +| ECharts | Options, `dataZoom`, `axisPointer`, actions, renderer state | Yes — `migrate-to-tanstack-charts` reference | +| D3 | Selections, scales, layouts, gestures, DOM lifecycle | Yes — `migrate-to-tanstack-charts` reference | +| React, Preact, Vue, Solid, Svelte, Angular, Lit, Alpine, Octane | Host mount, update, SSR, tooltip body composition, cleanup | No separate skill; targeted lifecycle references | +| React Native and `react-native-svg` | Native layout, text metrics, paint, accessibility actions, bundle boundaries | No separate skill; load its reference only for an explicit native target | +| TanStack Table and data grids | Row scopes, filters, grouping, pagination, selection, drill-down | Yes — `coordinate-charts-with-tanstack` table reference | +| TanStack Query, DB, and sync engines | Freshness, live projections, optimistic state, persistence, rollback | Yes — `coordinate-charts-with-tanstack` data reference | +| TanStack Router, Store, Virtual, Pacer, Start, and Form | Shared intent, URL state, render windows, scheduling, SSR, validated drafts | Yes — `coordinate-charts-with-tanstack` application-state reference | + +## Documentation Checklist + +All 100 canonical Markdown files under `docs/` were read. The generated `packages/charts-core/docs` copy was compared and deduplicated. All eight archived D3-core documents were read for migration evidence. + +
+Canonical docs (100) + +```text +docs/comparison.md +docs/concepts/chart-definitions.md +docs/concepts/data-and-channels.md +docs/concepts/grammar-of-graphics.md +docs/concepts/layout-axes-and-coordinates.md +docs/concepts/marks-and-layering.md +docs/concepts/scales-and-d3.md +docs/examples/annotations-and-overlays.md +docs/examples/bars-and-rankings.md +docs/examples/distributions.md +docs/examples/facets-and-multiple-views.md +docs/examples/heatmaps-and-densities.md +docs/examples/index.md +docs/examples/interactive-charts.md +docs/examples/intervals-and-financial.md +docs/examples/lines-and-areas.md +docs/examples/maps-and-spatial.md +docs/examples/networks-and-hierarchies.md +docs/examples/polar-and-radar.md +docs/examples/scatterplots-and-relationships.md +docs/examples/stacked-and-composition.md +docs/framework/alpine/adapter.md +docs/framework/alpine/reference/chart.md +docs/framework/angular/adapter.md +docs/framework/angular/reference/chart.md +docs/framework/lit/adapter.md +docs/framework/lit/reference/chart.md +docs/framework/octane/adapter.md +docs/framework/octane/quick-start.md +docs/framework/octane/reference/chart.md +docs/framework/preact/adapter.md +docs/framework/preact/reference/chart.md +docs/framework/react/adapter.md +docs/framework/react/quick-start.md +docs/framework/react/reference/chart.md +docs/framework/solid/adapter.md +docs/framework/solid/reference/chart.md +docs/framework/svelte/adapter.md +docs/framework/svelte/reference/chart.md +docs/framework/vue/adapter.md +docs/framework/vue/reference/chart.md +docs/guides/accessibility.md +docs/guides/ai-authoring.md +docs/guides/bundle-size-and-performance.md +docs/guides/choosing-a-chart.md +docs/guides/custom-marks-and-renderers.md +docs/guides/dynamic-data-and-animation.md +docs/guides/exporting.md +docs/guides/faceting-and-composition.md +docs/guides/interactions-and-selections.md +docs/guides/large-data.md +docs/guides/legends-and-color.md +docs/guides/migrating.md +docs/guides/responsive-charts.md +docs/guides/ssr-and-hydration.md +docs/guides/testing-and-debugging.md +docs/guides/themes-and-styling.md +docs/guides/tooltips-and-focus.md +docs/guides/transforms-and-reactivity.md +docs/guides/typescript.md +docs/installation.md +docs/overview.md +docs/quick-start.md +docs/reference/adapter-controller.md +docs/reference/chart-definitions.md +docs/reference/chart-spec.md +docs/reference/custom-extensions.md +docs/reference/dom-host.md +docs/reference/focus-and-interaction.md +docs/reference/index.md +docs/reference/marks/bar-and-rect.md +docs/reference/marks/box.md +docs/reference/marks/contour.md +docs/reference/marks/delaunay.md +docs/reference/marks/density.md +docs/reference/marks/difference.md +docs/reference/marks/dodge.md +docs/reference/marks/dot-and-hexagon.md +docs/reference/marks/focus-guide.md +docs/reference/marks/geo.md +docs/reference/marks/hexbin.md +docs/reference/marks/line-and-area.md +docs/reference/marks/polar.md +docs/reference/marks/regression.md +docs/reference/marks/ridgeline.md +docs/reference/marks/rules-links-arrows-vectors-and-ticks.md +docs/reference/marks/sankey.md +docs/reference/marks/sunburst.md +docs/reference/marks/text-frame-and-facet.md +docs/reference/marks/treemap.md +docs/reference/marks/violin.md +docs/reference/marks/voronoi.md +docs/reference/marks/waffle.md +docs/reference/motion.md +docs/reference/rendering-and-export.md +docs/reference/runtime-and-scene.md +docs/reference/scales-guides-and-color.md +docs/reference/transforms.md +docs/reference/types.md +docs/reference/view-composition.md +``` + +
+ +
+Archived D3-core docs (8) + +```text +packages/charts-core-d3/docs/AI-GUIDE.md +packages/charts-core-d3/docs/bundle-and-performance.md +packages/charts-core-d3/docs/custom-marks.md +packages/charts-core-d3/docs/dynamic-charts.md +packages/charts-core-d3/docs/observable-plot-migration.md +packages/charts-core-d3/docs/recipes.md +packages/charts-core-d3/docs/responsive-theme-accessibility.md +packages/charts-core-d3/docs/tanstack-stats-migration.md +``` + +
+ +Additional evidence read: root and client package READMEs, adapter implementations, package manifests and peer constraints, `API-FRICTION.md` F-001–F-257, relevant `CHANGELOG.md` migrations, all ten public GitHub issues found for the repository, and current official TanStack Table, Query, DB, Router, Store, Virtual, Pacer, Start, and Form guidance needed for ecosystem coordination. diff --git a/_artifacts/skill_tree.yaml b/_artifacts/skill_tree.yaml new file mode 100644 index 00000000..bd3136f8 --- /dev/null +++ b/_artifacts/skill_tree.yaml @@ -0,0 +1,282 @@ +# skill_tree.yaml +# Generated by TanStack Intent tree generation +# Library: @tanstack/charts +# Version: 0.9.0 +# Date: 2026-08-12 + +library: + name: '@tanstack/charts' + version: '0.9.0' + repository: 'https://github.com/TanStack/charts' + description: 'A typed, renderer-neutral visualization grammar for application charts.' + +generated_from: + domain_map: '_artifacts/domain_map.yaml' + skill_spec: '_artifacts/skill_spec.md' +generated_at: '2026-08-12' + +layout: + strategy: 'flat' + rationale: 'The twelve entries map one-to-one to distinct developer intents and should load independently.' + package: 'packages/charts-core' + skills_root: 'packages/charts-core/skills' + router_skill: false + +skills: + - name: 'Design a chart from a user goal' + slug: 'design-a-chart' + type: 'core' + domain: 'frame-the-visualization' + path: 'packages/charts-core/skills/design-a-chart/SKILL.md' + package: 'packages/charts-core' + description: >- + Choose an honest visualization from a user story, metric, comparison, + projection, target, or explanatory goal before selecting TanStack Charts + marks. Load for requests to graph, chart, visualize, compare, forecast, + project, or explain data. + sources: + - 'TanStack/charts:docs/guides/choosing-a-chart.md' + - 'TanStack/charts:docs/examples/*.md' + - 'TanStack/charts:docs/reference/transforms.md' + references: + - 'references/visual-task-matrix.md' + + - name: 'Prepare chart data' + slug: 'prepare-chart-data' + type: 'core' + domain: 'encode-application-data' + path: 'packages/charts-core/skills/prepare-chart-data/SKILL.md' + package: 'packages/charts-core' + description: >- + Order, aggregate, derive, and transform application rows for TanStack + Charts while preserving semantic units and lineage. Load for stacks, + bins, groups, rolling windows, ranks, regression, hierarchy, missing + values, or reactive transform work. + sources: + - 'TanStack/charts:docs/guides/transforms-and-reactivity.md' + - 'TanStack/charts:docs/reference/transforms.md' + - 'TanStack/charts:docs/reference/marks/line-and-area.md' + references: + - 'references/transforms.md' + + - name: 'Compose marks and views' + slug: 'compose-marks-and-views' + type: 'core' + domain: 'encode-application-data' + path: 'packages/charts-core/skills/compose-marks-and-views/SKILL.md' + package: 'packages/charts-core' + description: >- + Translate semantic encodings into TanStack Charts marks, layers, + annotations, facets, and coordinated views. Load when choosing marks, + combining layers, assigning interaction-point ownership, or deciding + whether a custom mark is justified. + sources: + - 'TanStack/charts:docs/concepts/grammar-of-graphics.md' + - 'TanStack/charts:docs/concepts/marks-and-layering.md' + - 'TanStack/charts:docs/guides/faceting-and-composition.md' + - 'TanStack/charts:docs/guides/custom-marks-and-renderers.md' + references: + - 'references/mark-selection.md' + + - name: 'Configure scales, guides, and color' + slug: 'configure-scales-guides-color' + type: 'core' + domain: 'encode-application-data' + path: 'packages/charts-core/skills/configure-scales-guides-color/SKILL.md' + package: 'packages/charts-core' + description: >- + Configure TanStack Charts domains, scale factories, axes, margins, + legends, grouping, and paint without taking ownership of responsive + ranges. Load for blank geometry, scale inference, color semantics, + shared domains, ticks, labels, or guide layout. + sources: + - 'TanStack/charts:docs/concepts/scales-and-d3.md' + - 'TanStack/charts:docs/reference/scales-guides-and-color.md' + - 'TanStack/charts:docs/guides/legends-and-color.md' + - 'TanStack/charts:docs/guides/responsive-charts.md' + references: + - 'references/scale-guide-ownership.md' + + - name: 'Design responsive charts' + slug: 'design-responsive-charts' + type: 'core' + domain: 'adapt-chart-behavior' + path: 'packages/charts-core/skills/design-responsive-charts/SKILL.md' + package: 'packages/charts-core' + description: >- + Make TanStack Charts adapt to measured containers, information priority, + guide margins, SSR initial width, and final plot bounds. Load for narrow + dashboards, overflow, label density, responsive topology, or + pixel-space layouts. + sources: + - 'TanStack/charts:docs/guides/responsive-charts.md' + - 'TanStack/charts:docs/reference/chart-definitions.md' + - 'TanStack/charts:docs/reference/marks/waffle.md' + - 'TanStack/charts:docs/reference/marks/treemap.md' + + - name: 'Build chart interactions' + slug: 'build-chart-interactions' + type: 'core' + domain: 'adapt-chart-behavior' + path: 'packages/charts-core/skills/build-chart-interactions/SKILL.md' + package: 'packages/charts-core' + description: >- + Compose TanStack Charts focus, tooltips, controlled selections, cursors, + brushes, zoom, keyboard behavior, and coordinated views. Load for hover, + focus, pinning, crosshairs, clipping, gesture state, or chart-table + coordination. + sources: + - 'TanStack/charts:docs/guides/tooltips-and-focus.md' + - 'TanStack/charts:docs/guides/interactions-and-selections.md' + - 'TanStack/charts:docs/reference/focus-and-interaction.md' + - 'TanStack/charts:docs/reference/marks/focus-guide.md' + references: + - 'references/interaction-state.md' + + - name: 'Update and animate charts' + slug: 'update-and-animate-charts' + type: 'core' + domain: 'adapt-chart-behavior' + path: 'packages/charts-core/skills/update-and-animate-charts/SKILL.md' + package: 'packages/charts-core' + description: >- + Update TanStack Charts through stable definition and datum identity, + configure SVG motion, and preserve correctness during resize, + interruption, streaming, and rolling windows. Load for reactive data, + animation, reordering, enter/exit, or stale interaction state. + sources: + - 'TanStack/charts:docs/guides/dynamic-data-and-animation.md' + - 'TanStack/charts:docs/reference/motion.md' + - 'TanStack/charts:docs/reference/chart-definitions.md' + + - name: 'Coordinate charts with TanStack' + slug: 'coordinate-charts-with-tanstack' + type: 'composition' + domain: 'adapt-chart-behavior' + path: 'packages/charts-core/skills/coordinate-charts-with-tanstack/SKILL.md' + package: 'packages/charts-core' + description: >- + Coordinate TanStack Charts with Table data grids, DB live queries and + sync engines, Query server state, Store, Router, Virtual, Pacer, Start, + and Form without duplicating data, state, revision, or lifecycle + ownership. + requires: + - 'prepare-chart-data' + - 'build-chart-interactions' + - 'update-and-animate-charts' + sources: + - 'TanStack/charts:docs/guides/interactions-and-selections.md' + - 'TanStack/charts:docs/guides/transforms-and-reactivity.md' + - 'https://tanstack.com/table/latest/docs/guide/row-models' + - 'https://tanstack.com/db/latest/docs/guides/live-queries' + - 'https://tanstack.com/db/latest/docs/guides/mutations' + - 'https://tanstack.com/query/latest/docs/framework/react/guides/important-defaults' + subsystems: + - 'TanStack Table and data grids' + - 'TanStack Query, DB, and sync engines' + - 'TanStack application state and lifecycle' + references: + - 'references/table-and-data-grid.md' + - 'references/query-db-and-sync.md' + - 'references/tanstack-application-state.md' + + - name: 'Migrate charts to TanStack Charts' + slug: 'migrate-to-tanstack-charts' + type: 'composition' + domain: 'ship-and-evolve-charts' + path: 'packages/charts-core/skills/migrate-to-tanstack-charts/SKILL.md' + package: 'packages/charts-core' + description: >- + Migrate Observable Plot, Recharts, Chart.js, ECharts, direct D3, or + another renderer to TanStack Charts by translating semantic ownership + and proving visual, interaction, accessibility, export, bundle, and + performance parity. + requires: + - 'design-a-chart' + - 'debug-and-verify-charts' + sources: + - 'TanStack/charts:docs/guides/migrating.md' + - 'TanStack/charts:packages/charts-core-d3/docs/observable-plot-migration.md' + - 'TanStack/charts:packages/charts-core-d3/docs/tanstack-stats-migration.md' + subsystems: + - 'Observable Plot' + - 'Recharts' + - 'Chart.js' + - 'ECharts' + - 'D3' + references: + - 'references/observable-plot.md' + - 'references/recharts.md' + - 'references/chart-js.md' + - 'references/echarts.md' + - 'references/d3.md' + + - name: 'Ship accessible charts' + slug: 'ship-accessible-charts' + type: 'lifecycle' + domain: 'ship-and-evolve-charts' + path: 'packages/charts-core/skills/ship-accessible-charts/SKILL.md' + package: 'packages/charts-core' + description: >- + Ship TanStack Charts with meaningful accessibility, deterministic SSR + and hydration, correct adapter lifecycle, renderer and export choices, + exact package subpaths, and explicit React Native validation. + sources: + - 'TanStack/charts:docs/guides/accessibility.md' + - 'TanStack/charts:docs/guides/ssr-and-hydration.md' + - 'TanStack/charts:docs/guides/exporting.md' + - 'TanStack/charts:docs/framework/*/adapter.md' + - 'TanStack/charts:packages/react-native-charts/README.md' + subsystems: + - 'DOM framework adapters' + - 'React Native' + - 'Renderers and export' + references: + - 'references/react.md' + - 'references/preact.md' + - 'references/vue.md' + - 'references/solid.md' + - 'references/svelte.md' + - 'references/angular.md' + - 'references/lit.md' + - 'references/alpine.md' + - 'references/octane.md' + - 'references/react-native.md' + - 'references/renderers-and-export.md' + + - name: 'Debug and verify charts' + slug: 'debug-and-verify-charts' + type: 'lifecycle' + domain: 'ship-and-evolve-charts' + path: 'packages/charts-core/skills/debug-and-verify-charts/SKILL.md' + package: 'packages/charts-core' + description: >- + Diagnose TanStack Charts type, data, scale, geometry, focus, lifecycle, + bundle, and performance failures with scenario-level evidence. Load for + blank or clipped charts, misleading output, unsafe casts, migration + parity, packed-package checks, or regressions. + sources: + - 'TanStack/charts:docs/guides/testing-and-debugging.md' + - 'TanStack/charts:docs/guides/typescript.md' + - 'TanStack/charts:docs/guides/bundle-size-and-performance.md' + - 'TanStack/charts:API-FRICTION.md' + + - name: 'Extend TanStack Charts' + slug: 'extend-tanstack-charts' + type: 'core' + domain: 'ship-and-evolve-charts' + path: 'packages/charts-core/skills/extend-tanstack-charts/SKILL.md' + package: 'packages/charts-core' + description: >- + Implement TanStack Charts custom marks, scale-value contracts, + final-bounds layouts, renderer-neutral scene nodes, renderers, hosts, + controls, or composed views after built-in primitives are exhausted. + requires: + - 'compose-marks-and-views' + sources: + - 'TanStack/charts:docs/guides/custom-marks-and-renderers.md' + - 'TanStack/charts:docs/reference/custom-extensions.md' + - 'TanStack/charts:packages/charts-core/src/mark.ts' + - 'TanStack/charts:packages/charts-core/src/mark-with-scale-values.ts' + references: + - 'references/extension-protocols.md' diff --git a/benchmarks/conformance/previews/manifest.json b/benchmarks/conformance/previews/manifest.json index 5f0cc46f..29269508 100644 --- a/benchmarks/conformance/previews/manifest.json +++ b/benchmarks/conformance/previews/manifest.json @@ -2,7 +2,7 @@ "schemaVersion": 1, "width": 288, "height": 192, - "sourceHash": "61107a358d4bc33d970663907eb733cf5babfa82a20b18abe4fbcc5e455fee3f", + "sourceHash": "e51640f5fc66d37a982722a282685167d084940132c429f7a2515f16f28714f3", "assets": [ { "id": "01-line-gaps", diff --git a/packages/charts-core/package.json b/packages/charts-core/package.json index 5569a0be..16217578 100644 --- a/packages/charts-core/package.json +++ b/packages/charts-core/package.json @@ -3,6 +3,9 @@ "version": "0.11.1", "private": false, "license": "MIT", + "keywords": [ + "tanstack-intent" + ], "repository": { "type": "git", "url": "https://github.com/TanStack/charts.git", @@ -13,6 +16,7 @@ "files": [ "dist", "docs", + "skills", "LICENSE", "README.md", "llms.txt" @@ -95,6 +99,7 @@ } }, "devDependencies": { + "@tanstack/intent": "^0.3.6", "@types/d3-array": "^3.2.2", "@types/d3-brush": "^3.0.6", "@types/d3-contour": "^3.0.6", diff --git a/packages/charts-core/skills/build-chart-interactions/SKILL.md b/packages/charts-core/skills/build-chart-interactions/SKILL.md new file mode 100644 index 00000000..2bda97d2 --- /dev/null +++ b/packages/charts-core/skills/build-chart-interactions/SKILL.md @@ -0,0 +1,222 @@ +--- +name: build-chart-interactions +description: > + Compose TanStack Charts focus, tooltips, controlled selections, cursors, + brushes, zoom, keyboard behavior, and coordinated views. Load for hover, + focus, pinning, crosshairs, clipping, gesture state, or chart-table + coordination. +metadata: + type: core + library: '@tanstack/charts' + library_version: '0.9.0' +sources: + - 'TanStack/charts:docs/guides/tooltips-and-focus.md' + - 'TanStack/charts:docs/guides/interactions-and-selections.md' + - 'TanStack/charts:docs/reference/focus-and-interaction.md' + - 'TanStack/charts:docs/reference/marks/focus-guide.md' +--- + +# Build Chart Interactions + +Use **trigger → inspect → decide → build → verify**. Charts owns renderer-neutral interaction mechanics; the application owns accepted semantic state, persistence, product policy, and equivalent non-pointer controls. + +## Setup + +Start with native focus and tooltip behavior before adding controlled state: + +```ts +import { defineChart, lineY } from '@tanstack/charts' +import { crosshair } from '@tanstack/charts/crosshair' +import { scaleLinear } from '@tanstack/charts/scales/linear' +import { scalePoint } from '@tanstack/charts/scales/point' +import { tooltip } from '@tanstack/charts/tooltip' + +const rows = [ + { week: 'May 4', value: 820 }, + { week: 'May 11', value: 960 }, + { week: 'May 18', value: 1_140 }, +] + +export const chart = defineChart({ + marks: [ + lineY(rows, { x: 'week', y: 'value', points: true }), + crosshair({ x: { label: true }, y: false }), + ], + x: { scale: scalePoint }, + y: { scale: scaleLinear }, + focus: 'nearest-x', + maxFocusDistance: Number.POSITIVE_INFINITY, + tooltip, +}) +``` + +Keep the finite default focus distance when empty space should clear inspection. + +## Core Patterns + +### Choose interaction ownership + +| Need | Owner | +| ---------------------------------------------------------------------------- | ------------------------------------------ | +| Nearest datum, grouped tooltip, snapped crosshair, keyboard point navigation | chart focus | +| Semantic selection, free cursor, handle, brush, zoom, interactive legend | first-party control plus controlled signal | +| Shared accepted range, persistence, playback, editing, rich pinned details | application state/UI | + +Read [the interaction state matrix](references/interaction-state.md) before combining controls. + +### Coordinate chart and application selection by key + +```ts +import { defineChart, dot } from '@tanstack/charts' +import { controlledSignal } from '@tanstack/charts/interaction/signal' +import { scaleLinear } from '@tanstack/charts/scales/linear' +import { keyedSelection, whenSelected } from '@tanstack/charts/selection' + +const observations = [ + { id: 'a', speed: 12, efficiency: 32 }, + { id: 'b', speed: 18, efficiency: 27 }, +] + +let selectedId: string | null = null + +const selection = keyedSelection< + (typeof observations)[number], + string, + number, + number +>({ + selected: controlledSignal(selectedId, (next) => { + selectedId = next + }), + key: (datum) => datum.id, +}) + +export const chart = defineChart({ + marks: [ + dot(observations, { + id: 'observations', + x: 'speed', + y: 'efficiency', + key: 'id', + }), + whenSelected( + dot(observations, { + id: 'selected-observation', + x: 'speed', + y: 'efficiency', + key: 'id', + r: 7, + }), + selection, + ), + ], + x: { scale: scaleLinear }, + y: { scale: scaleLinear }, + selection, +}) +``` + +Rebuild the definition with the accepted controlled value. A signal is a snapshot and callback, not a hidden store. + +### Synchronize semantic values, not pixels + +Use one `createChartCursor` controller across definitions when charts should resolve the same x/y value through their own scales. Keep crosshair presentation in each definition. Never copy DOM coordinates or mutate another chart's SVG. + +### Add portaling only for containment boundaries + +Use native tooltip content first. Add `portal` when overflow, transforms, or stacking contexts clip the surface. Use framework adapter tooltip bodies only when the product requires rich interactive content. + +## Common Mistakes + +### CRITICAL Mutating SVG for focus presentation + +Wrong: + +```ts +onRender={({ svg }) => svg.insertBefore(activeBand, svg.firstChild)} +``` + +Correct: + +```ts +marks: [ + whenFocused(bandX(rows, { x: 'date' }), { match: 'x' }), + lineY(rows, channels), +] +``` + +DOM mutation bypasses scene identity, SSR, Canvas, React Native, motion, and cleanup. Current focus marks replace an older workaround that still appears in issue examples. + +Source: GitHub issue 9; `API-FRICTION.md` F-178 + +### HIGH Focusing a point-less rule + +Wrong: + +```ts +whenFocused(ruleX(dates), { match: 'x' }) +``` + +Correct: + +```ts +focusGuideX(rows, { x: 'date', y: 'value', xRule: {} }) +``` + +Rules emit no interaction points, so a focus filter has no candidate identity. Current focus-guide primitives replace this legacy pattern. + +Source: GitHub issue 32; `API-FRICTION.md` F-237 + +### CRITICAL Treating callbacks as complete behavior + +Wrong: attach only an `onRangeChange` or key callback to an overlay. + +Correct: use the matching controlled control (`keyedSelection`, `continuousCursor`, `handleX`, `brushX`, or `zoomX`) and store its accepted semantic value in application state. + +A callback alone does not define capture, clamping, cancellation, keyboard operations, or ownership. + +Source: `API-FRICTION.md` F-075; `docs/guides/interactions-and-selections.md` + +### HIGH Keeping tooltips inside clipped ancestors + +Wrong: + +```ts +tooltip: { + use: tooltip +} +``` + +Correct: + +```ts +tooltip: { + use: (tooltip, portal) +} +``` + +Overflow, transforms, and stacking contexts can trap a correctly positioned tooltip. + +Source: `API-FRICTION.md` F-133; `docs/guides/tooltips-and-focus.md` + +### HIGH Letting decorative layers own duplicate points + +Wrong: make every area, line, dot, label, and highlight layer over one observation independently focusable. + +Correct: choose one semantic interaction owner and use `decorative`, `whenFocused`, or `whenSelected` for supporting presentation. + +Duplicate points create repeated keyboard stops, focus candidates, activations, and tooltip rows. + +Source: `API-FRICTION.md` F-218; `docs/guides/tooltips-and-focus.md` + +### HIGH Tension: rich interaction versus portable rendering + +Prefer marks, controls, semantic state, and host extensions over DOM-only overlays. Verify pointer and keyboard paths, static fallback, teardown, and any native equivalent. + +See also: `ship-accessible-charts/SKILL.md` and `extend-tanstack-charts/SKILL.md` + +## References + +- [Interaction ownership and state machines](references/interaction-state.md) + +See also: `coordinate-charts-with-tanstack/SKILL.md`, `ship-accessible-charts/SKILL.md`, and `update-and-animate-charts/SKILL.md` — interaction requires explicit cross-surface ownership, keyboard parity, and stable identity across updates. diff --git a/packages/charts-core/skills/build-chart-interactions/references/interaction-state.md b/packages/charts-core/skills/build-chart-interactions/references/interaction-state.md new file mode 100644 index 00000000..e82f359c --- /dev/null +++ b/packages/charts-core/skills/build-chart-interactions/references/interaction-state.md @@ -0,0 +1,53 @@ +# Interaction Ownership and State + +## Primitive Matrix + +| User task | Primitive | Semantic state | Presentation | Non-pointer requirement | +| ----------------------------- | ------------------------------------- | --------------------------------------- | --------------------------- | ---------------------------------------- | +| Inspect nearest datum | focus strategy | chart focus point/group | focus ring, tooltip | native point navigation | +| Compare series at one x/y | `group-x` / `group-y` focus | focused group | grouped tooltip/crosshair | same grouping by keyboard | +| Mirror focused value | `whenFocused`, focus guide, crosshair | existing focus | authored or data-less guide | does not add a stop | +| Synchronize charts | `createChartCursor` + `cursorHost` | shared semantic x/y and optional origin | local crosshair per chart | programmatic/keyboard source retained | +| Track arbitrary plot position | free cursor / `continuousCursor` | numeric/temporal position | crosshair and labels | pair with labeled inputs or status | +| Select a datum | `keyedSelection` | stable datum key | `whenSelected` overlay | Enter/Space and clear path | +| Toggle series | `interactiveColorLegend` | visible color-domain values | pressed legend controls | native buttons | +| Choose one scale value | `handleX` | one semantic value | handle/control fallback | labeled input or keyboard control | +| Choose a range | `brushX` | semantic domain | brush control/fallback | application range controls when required | +| Navigate a window | `zoomX` | semantic viewport domain | pan/zoom host behavior | buttons/inputs for critical navigation | +| Show compact details | built-in `tooltip` | current/pinned focus | native text surface | focus and Escape/pin behavior | +| Show rich details | adapter tooltip body | current/pinned focus | framework UI | focus containment and action labels | + +## Controlled-State Contract + +1. The behavior proposes the next complete semantic value. +2. The application accepts, rejects, clamps, persists, or broadcasts it. +3. The application rebuilds the definition with the accepted snapshot. +4. Stable controller, mark, and datum identities preserve focus through the update. + +Do not treat a controlled signal as a subscription store. Do not let a DOM gesture own canonical scale domains. + +## Focus Decision + +| Question | Choice | +| ----------------------------------------------- | -------------------------------------- | +| One nearest painted point | default focus | +| Prioritize one axis without grouping | `nearest-x` / `nearest-y` | +| Compare every series at one x/y | `group-x` / `group-y` | +| Snap through sparse points across empty plot | explicitly infinite `maxFocusDistance` | +| Empty space should clear inspection | finite default distance | +| Paint one native guide at current focus | `crosshair` | +| Reveal authored geometry matching focused value | `whenFocused` | +| Need a rule plus point identity | focus-guide mark, not a bare rule | + +## Verification Scenarios + +- Pointer enter, move, leave, click/tap pin, and Escape/cancel. +- Keyboard forward/backward traversal, Enter/Space activation, and clear. +- Duplicate x values, duplicate keys, facets, and hidden series. +- Data reorder, removal of focused datum, and controlled-state rejection. +- Clipped/translated ancestors and portal cleanup. +- Resize or animation while focus is active. +- Multiple linked charts with missing local values. +- Static rendering and renderer-native fallback. + +Source: `docs/guides/tooltips-and-focus.md`; `docs/guides/interactions-and-selections.md`; `docs/reference/focus-and-interaction.md` diff --git a/packages/charts-core/skills/compose-marks-and-views/SKILL.md b/packages/charts-core/skills/compose-marks-and-views/SKILL.md new file mode 100644 index 00000000..438ceb31 --- /dev/null +++ b/packages/charts-core/skills/compose-marks-and-views/SKILL.md @@ -0,0 +1,183 @@ +--- +name: compose-marks-and-views +description: > + Translate semantic encodings into TanStack Charts marks, layers, + annotations, facets, and coordinated views. Load when choosing marks, + combining layers, assigning interaction-point ownership, or deciding + whether a custom mark is justified. +metadata: + type: core + library: '@tanstack/charts' + library_version: '0.9.0' +sources: + - 'TanStack/charts:docs/concepts/grammar-of-graphics.md' + - 'TanStack/charts:docs/concepts/marks-and-layering.md' + - 'TanStack/charts:docs/guides/faceting-and-composition.md' + - 'TanStack/charts:docs/guides/custom-marks-and-renderers.md' +--- + +# Compose Marks and Views + +Use **trigger → inspect → decide → build → verify**. A chart type is usually a composition of marks with shared scales, not a component to look up by name. + +## Setup + +Layer broad context first and direct evidence last: + +```ts +import { areaY, defineChart, dot, lineY, ruleY, text } from '@tanstack/charts' +import { scaleLinear } from '@tanstack/charts/scales/linear' +import { scalePoint } from '@tanstack/charts/scales/point' + +const rows = [ + { month: 'Jan', low: 72, value: 80, high: 89, label: '' }, + { month: 'Feb', low: 77, value: 86, high: 96, label: '' }, + { month: 'Mar', low: 84, value: 94, high: 106, label: 'Launch' }, +] + +export const chart = defineChart({ + marks: [ + areaY(rows, { id: 'range', x: 'month', y1: 'low', y2: 'high' }), + ruleY([90], { id: 'target' }), + lineY(rows, { id: 'trend', x: 'month', y: 'value' }), + dot( + rows.filter((row) => row.label), + { id: 'events', x: 'month', y: 'value' }, + ), + text( + rows.filter((row) => row.label), + { + id: 'event-labels', + x: 'month', + y: 'value', + text: 'label', + dy: -10, + }, + ), + ], + x: { scale: scalePoint }, + y: { scale: scaleLinear }, +}) +``` + +Declaration order is paint order. Each mark may use a different datum type. + +## Core Patterns + +### Give one layer interaction ownership + +```ts +import { defineChart, dot, lineY } from '@tanstack/charts' +import { decorative } from '@tanstack/charts/mark/decorative' + +const rows = [ + { id: 'jan', month: 'Jan', value: 80 }, + { id: 'feb', month: 'Feb', value: 86 }, +] + +export const chart = defineChart({ + marks: [ + decorative(lineY(rows, { x: 'month', y: 'value' })), + dot(rows, { x: 'month', y: 'value', key: 'id' }), + ], +}) +``` + +`decorative` keeps geometry and scale materialization but removes duplicate focus, tooltip, and activation points. + +### Use explicit geometry groups + +```ts +import { lineY } from '@tanstack/charts' + +const rows = [ + { date: '2026-08-01', region: 'North', value: 42, status: 'healthy' }, + { date: '2026-08-01', region: 'South', value: 37, status: 'healthy' }, +] + +export const lines = lineY(rows, { + x: 'date', + y: 'value', + z: 'region', + color: 'status', +}) +``` + +Keep geometry identity (`z`) separate from paint semantics (`color`) when they differ. + +### Choose the smallest complete composition + +Escalate in this order: + +1. One built-in or first-party composite mark. +2. Several built-in marks sharing scales. +3. Facets or named views. +4. D3-prepared rows passed to built-in marks. +5. `compositeMark` built from ordinary marks. +6. A custom mark that emits renderer-neutral scene nodes. +7. An application-owned overlay or gesture controller. + +Read [the mark-selection matrix](references/mark-selection.md) before creating a custom mark. + +### Use facets for repeated questions, views for distinct roles + +Use `facetChart` when every panel asks the same question over a group. Use `composeViews` for focus-and-context, marginal summaries, or panels with distinct roles. Keep semantic shared state in the application; do not synchronize views through DOM nodes. + +## Common Mistakes + +### CRITICAL Encoding the chart as one custom path + +Wrong: emit one path containing the complete visualization. + +Correct: retain mark-local data, channels, scales, and interaction points; extend only the geometry that built-ins cannot express. + +A monolithic path discards automatic domains, typed rows, portable rendering, focus ownership, and composability. + +Source: `docs/concepts/grammar-of-graphics.md`; `docs/guides/custom-marks-and-renderers.md` + +### MEDIUM Expecting an area to draw its line + +Wrong: + +```ts +marks: [areaY(rows, { x: 'date', y: 'value' })] +``` + +Correct: + +```ts +marks: [ + areaY(rows, { x: 'date', y: 'value' }), + lineY(rows, { x: 'date', y: 'value' }), +] +``` + +Area and line are independent layers with independent style, state, and point ownership. + +Source: `docs/reference/marks/line-and-area.md` + +### HIGH Letting decorative layers own duplicate points + +Wrong: layer line, area, dots, and labels over the same rows with every mark interactive. + +Correct: choose the semantic owner and wrap always-painted supporting marks with `decorative`. + +Duplicate points create repeated keyboard stops, focus candidates, and tooltip rows. + +Source: `API-FRICTION.md` F-218; `docs/guides/tooltips-and-focus.md` + +### HIGH Rebuilding supported coordinates in userland + +Wrong: project polar, spatial, hierarchy, or network geometry into arbitrary pixels before chart layout. + +Correct: use first-party coordinate/layout primitives, or a resolved-layout mark when final bounds are truly required. + +Userland projection hides responsive final-bounds work and duplicates capability modules. Current first-party primitives fixed many early gaps, but old examples still encourage application-owned engines. + +Source: `API-FRICTION.md` F-117, F-199–F-208; `docs/examples/index.md` + +## References + +- [Mark selection and composition matrix](references/mark-selection.md) + +See also: `build-chart-interactions/SKILL.md` and `extend-tanstack-charts/SKILL.md` — layering determines point ownership, and native composition should be exhausted before extension. diff --git a/packages/charts-core/skills/compose-marks-and-views/references/mark-selection.md b/packages/charts-core/skills/compose-marks-and-views/references/mark-selection.md new file mode 100644 index 00000000..d7ce76c3 --- /dev/null +++ b/packages/charts-core/skills/compose-marks-and-views/references/mark-selection.md @@ -0,0 +1,54 @@ +# Mark Selection and Composition Matrix + +Choose by semantic ownership, not visual resemblance. Read the corresponding mark reference for exact channels. + +| Need | Start with | Compose when | Ownership check | +| -------------------------------- | ---------------------------------- | ------------------------------------------------- | ----------------------------------------------------------- | +| Trend | `lineY` / `lineX` | add dots, interval area, target rule, annotations | input order is path order; `z` partitions paths | +| Range or uncertainty | `areaY` / `areaX` | add boundary lines and observed/projected status | explicit endpoints carry interval meaning | +| Difference between paths | `differenceY` / `differenceX` | add source lines and zero rule | difference owns crossover geometry | +| Category magnitude | `barX` / `barY` | add target rule, labels, selected overlay | zero baseline and stack/group policy | +| General interval or heatmap cell | `rect` / `cell` | add labels or boundaries | both endpoints materialize scales | +| Relationship or observation | `dot` / `hexagon` | regression, density, selected overlay | one stable point per observation | +| Distribution summary | `boxX` / `boxY`, violin, ridgeline | facets for groups, raw dots when useful | composite retains quartiles/outliers/lineage | +| Threshold or baseline | `ruleX` / `ruleY` | text label or interval band | usually decorative unless it is selectable data | +| Annotation | `text`, dot, rule, rect, link | prepare sparse annotation rows | selection rule belongs in data preparation | +| Directed relation | `arrow`, `link`, `vector` | nodes, labels, edge emphasis | endpoints and direction are explicit channels | +| Polar comparison | polar capability marks | center labels, reference rings | angle, radius, and cyclic order are semantic | +| Geographic shape | `geoShape` and geo capability | points, labels, graticule | projection and spatial unit are explicit | +| Density/partition | hexbin, contour, density | raw samples only when readable | final bounds can change topology | +| Hierarchy | treemap, sunburst, hierarchy tree | sparse labels and selection | parent-child integrity and root policy | +| Flow | Sankey | node/edge labels and controlled selection | conservation, source/target identity | +| Network | force/link primitives | selected neighborhood or matrix alternative | link semantics and stable node keys | +| Repeated comparison | `facetChart` / `facet` | shared guides and explicit domain policy | same question and panel grammar | +| Distinct coordinated roles | `composeViews` | `shareX/Y` or `alignX/Y` links | one figure if one accessible task; separate hosts otherwise | + +## Layer Order + +1. Background regions and filled areas. +2. Reference bands and rules. +3. Primary bars, paths, or cells. +4. Highlight points, ticks, links, or vectors. +5. Labels and annotations. + +## Interaction Ownership + +For layered marks representing the same observation: + +1. Pick the layer whose geometry best represents the selectable datum. +2. Keep its stable `id` and datum `key`. +3. Wrap always-painted supporting layers in `decorative`. +4. Use focused/selected conditional marks for state presentation without duplicate points. +5. Verify pointer and keyboard traversal report one semantic observation once. + +## Facet Versus View + +| Condition | Use | +| -------------------------------------------------- | -------------------------------- | +| Same grammar repeated by one grouping field | facet | +| Same positional meaning must compare across panels | shared configured domains | +| Independent domains reveal local structure | cell axes plus explicit labeling | +| Panels have different roles or mark grammars | named views | +| Panels need independent tooltips or labels | separate hosts | + +Source: `docs/concepts/marks-and-layering.md`; `docs/guides/faceting-and-composition.md` diff --git a/packages/charts-core/skills/configure-scales-guides-color/SKILL.md b/packages/charts-core/skills/configure-scales-guides-color/SKILL.md new file mode 100644 index 00000000..3edf34a7 --- /dev/null +++ b/packages/charts-core/skills/configure-scales-guides-color/SKILL.md @@ -0,0 +1,200 @@ +--- +name: configure-scales-guides-color +description: > + Configure TanStack Charts domains, scale factories, axes, margins, + legends, grouping, and paint without taking ownership of responsive ranges. + Load for blank geometry, scale inference, color semantics, shared domains, + ticks, labels, or guide layout. +metadata: + type: core + library: '@tanstack/charts' + library_version: '0.9.0' +sources: + - 'TanStack/charts:docs/concepts/scales-and-d3.md' + - 'TanStack/charts:docs/reference/scales-guides-and-color.md' + - 'TanStack/charts:docs/guides/legends-and-color.md' + - 'TanStack/charts:docs/guides/responsive-charts.md' +--- + +# Configure Scales, Guides, and Color + +Use **trigger → inspect → decide → build → verify**. The application owns semantic domains and color policy; TanStack Charts owns inferred domains, responsive positional ranges, guide measurement, and final paint resolution. + +## Setup + +Use factories for inferred domains and configured instances for fixed semantic domains: + +```ts +import { colorLegend, defineChart, lineY } from '@tanstack/charts' +import { scaleLinear } from '@tanstack/charts/scales/linear' +import { scaleOrdinal } from '@tanstack/charts/scales/ordinal' +import { scalePoint } from '@tanstack/charts/scales/point' + +const rows = [ + { week: 'May 4', package: 'core', downloads: 820 }, + { week: 'May 11', package: 'core', downloads: 960 }, + { week: 'May 4', package: 'react', downloads: 610 }, + { week: 'May 11', package: 'react', downloads: 730 }, +] + +const color = scaleOrdinal() + .domain(['core', 'react']) + .range(['#2563eb', '#f97316']) + +export const chart = defineChart({ + marks: [lineY(rows, { x: 'week', y: 'downloads', z: 'package' })], + x: { scale: () => scalePoint().padding(0.2) }, + y: { + scale: scaleLinear, + nice: true, + grid: true, + axis: { label: 'Downloads' }, + }, + color: { scale: color, legend: colorLegend({ label: 'Package' }) }, +}) +``` + +## Core Patterns + +### Choose a scale by semantics + +- Numeric position → compact `scaleLinear`. +- Categories with width → compact `scaleBand`. +- Categories without width → compact `scalePoint`. +- Stable categorical paint → compact `scaleOrdinal`. +- Elapsed time, nonlinear transforms, radial mapping, statistical bins, or continuous color → exact `d3-scale` family. + +Read [the ownership matrix](references/scale-guide-ownership.md) before configuring a D3 instance. + +### Share domains intentionally + +```ts +import { scaleLinear } from '@tanstack/charts/scales/linear' + +export const percentScale = scaleLinear().domain([0, 1]) + +export const sharedPercentAxis = { + scale: percentScale, + axis: { + label: 'Conversion rate', + ticks: { format: (value: number) => `${Math.round(value * 100)}%` }, + }, +} +``` + +A configured instance keeps the domain stable across filtering, facets, or linked views. Charts copies it and assigns the current range. + +### Keep series identity separate from paint + +```ts +import { lineY } from '@tanstack/charts' + +const rows = [ + { date: '2026-08-01', series: 'api', status: 'healthy', value: 91 }, + { date: '2026-08-01', series: 'worker', status: 'healthy', value: 84 }, +] + +export const mark = lineY(rows, { + x: 'date', + y: 'value', + z: 'series', + color: 'status', +}) +``` + +Use `z` for geometry groups, `color` for semantic scale values, and `fill`/`stroke` for final local paint overrides. + +### Separate tick candidates from label collision + +Tick count, spacing, or values chooses candidates. Label thinning, rotation, formatting, and priority decide which candidate labels remain readable. Automatic margins contain guides; they do not make every label legible. + +## Common Mistakes + +### CRITICAL Assigning positional pixel ranges + +Wrong: + +```ts +x: { + scale: scaleLinear().range([0, 640]) +} +``` + +Correct: + +```ts +x: { + scale: scaleLinear +} +``` + +The final plot range changes after container measurement and guide margins resolve. + +Source: `API-FRICTION.md` F-002; `docs/concepts/scales-and-d3.md` + +### CRITICAL Using a default instance for inference + +Wrong: + +```ts +y: { + scale: scaleLinear() +} +``` + +Correct: + +```ts +y: { + scale: scaleLinear +} +``` + +A scale instance owns its domain; the factory delegates domain inference to chart channels. + +Source: `CHANGELOG.md` 0.0.1 migration; `docs/concepts/scales-and-d3.md` + +### HIGH Using color as geometry identity accidentally + +Wrong: + +```ts +lineY(rows, { x: 'date', y: 'value', color: 'status' }) +``` + +Correct: + +```ts +lineY(rows, { + x: 'date', + y: 'value', + z: 'series', + color: 'status', +}) +``` + +When `z` is absent, a discrete color channel can also partition connected geometry. + +Source: `API-FRICTION.md` F-009, F-013; `docs/concepts/data-and-channels.md` + +### HIGH Treating containment as collision avoidance + +Wrong: rely on automatic margins to solve dense tick labels. + +Correct: define candidate spacing, thinning priority, rotation, abbreviation, or a different responsive composition. + +Margins keep guides inside the surface; they do not guarantee labels avoid each other. + +Source: `API-FRICTION.md` F-023, F-160; `docs/guides/responsive-charts.md` + +### HIGH Tension: responsive adaptation versus comparison stability + +Reduce labels or change composition at narrow widths, but do not silently change a shared domain, threshold, or category-color assignment to make the chart fit. + +See also: `design-responsive-charts/SKILL.md` § Common Mistakes + +## References + +- [Scale, guide, grouping, and color ownership](references/scale-guide-ownership.md) + +See also: `design-responsive-charts/SKILL.md` and `debug-and-verify-charts/SKILL.md` — final ranges depend on layout, and blank charts often expose scale-contract failures. diff --git a/packages/charts-core/skills/configure-scales-guides-color/references/scale-guide-ownership.md b/packages/charts-core/skills/configure-scales-guides-color/references/scale-guide-ownership.md new file mode 100644 index 00000000..4e06f08a --- /dev/null +++ b/packages/charts-core/skills/configure-scales-guides-color/references/scale-guide-ownership.md @@ -0,0 +1,62 @@ +# Scale and Guide Ownership + +## Scale Input Decision + +| Requirement | Supply | Owner | +| ---------------------------------------------------- | ------------------------------------------------ | -------------------------------------------------------- | +| Domain follows rendered mark channels | scale factory | Charts infers domain and assigns range | +| Configure padding before inferred categorical domain | zero-argument factory returning configured scale | application owns padding; Charts owns domain/range | +| Domain is product state or shared comparison policy | configured scale instance | application owns domain; Charts copies and assigns range | +| Reverse visual direction | axis `reverse` | Charts reverses range without rewriting domain | +| Nice inferred continuous domain | axis `nice` | Charts applies after domain inference | +| Numeric or categorical basics | compact exact subpath | application chooses family | +| Elapsed time or nonlinear mapping | exact `d3-scale` family | application declares direct dependency | + +Never assign positional pixel ranges in authored chart code. + +## Scale Family Decision + +| Meaning | Start with | Upgrade condition | +| ---------------------------------- | ------------------------------------ | ------------------------------------------------------------- | +| Numeric position | `@tanstack/charts/scales/linear` | piecewise, nonnumeric interpolation, log/power/symlog/radial | +| Categorical position with width | `@tanstack/charts/scales/band` | required behavior exceeds compact contract | +| Categorical position without width | `@tanstack/charts/scales/point` | elapsed time matters | +| Stable categorical paint | `@tanstack/charts/scales/ordinal` | sequential/diverging/quantile/threshold semantics | +| Calendar time | `d3-scale` `scaleTime` or `scaleUtc` | always; point/band dates do not preserve elapsed duration | +| Sequential/diverging quantity | D3 color scale | center, interpolation, or statistical thresholds are semantic | + +## Guide Decision + +| Need | Configure | Verify | +| ---------------------------- | ------------------------------------- | --------------------------------------------------------- | +| Fewer or more candidates | `ticks.count` or `ticks.spacing` | actual scale may return a different count | +| Exact semantic candidates | `ticks.values` | kept labels outside candidates do not add grid/tick stubs | +| Collision policy | `tickLabels.thin` | smallest supported width | +| Rotated labels | `tickLabels.rotate` and anchor policy | measured margins and readable baseline | +| Preserve endpoints or events | `thin.keep` / priority | hard-kept labels may collide with each other | +| Hide guide but retain scale | `axis: false` | marks still materialize scale | +| Hide grid separately | `grid: false` | guide and grid are independent | +| Shared comparison | configured instances/domains | filtering does not rescale meaning | + +## Color Ownership + +| Channel/config | Meaning | +| ---------------------- | ----------------------------------------------------------- | +| `z` | geometry and interaction group identity | +| mark `color` | value sent through chart color scale and legend | +| mark `fill` / `stroke` | final paint override; bypasses scale mapping for that paint | +| chart `color.scale` | categorical or quantitative mapping | +| chart `color.legend` | visual explanation of the resolved color scale | + +Use a stable configured ordinal domain when a product category must retain its color across filtering, reordering, sessions, or views. Use sequential color for ordered magnitude and diverging color only around a meaningful center. Keep essential state available without color. + +## Failure Diagnosis Order + +1. Confirm every materialized x/y channel has the matching axis scale. +2. Confirm the scale domain type matches channel values. +3. Distinguish factory inference from instance-owned domain. +4. Check finite values, log constraints, and missing endpoints. +5. Inspect the resolved domain and final range. +6. Inspect guide margins and clipping separately from the scale. + +Source: `docs/concepts/scales-and-d3.md`; `docs/reference/scales-guides-and-color.md`; `docs/guides/legends-and-color.md` diff --git a/packages/charts-core/skills/coordinate-charts-with-tanstack/SKILL.md b/packages/charts-core/skills/coordinate-charts-with-tanstack/SKILL.md new file mode 100644 index 00000000..621fa9bb --- /dev/null +++ b/packages/charts-core/skills/coordinate-charts-with-tanstack/SKILL.md @@ -0,0 +1,210 @@ +--- +name: coordinate-charts-with-tanstack +description: > + Coordinate TanStack Charts with TanStack Table data grids, TanStack DB live + queries and sync engines, TanStack Query server state, Store, Router, + Virtual, Pacer, Start, and Form without duplicating ownership. Load for + chart-grid linking, shared filtering or selection, live synchronized data, + optimistic updates, URL-restorable chart state, virtualized detail views, + paced streams, or dashboard-wide state. +metadata: + type: composition + library: '@tanstack/charts' + library_version: '0.9.0' +sources: + - 'TanStack/charts:docs/guides/interactions-and-selections.md' + - 'TanStack/charts:docs/guides/transforms-and-reactivity.md' + - 'TanStack/charts:docs/guides/dynamic-data-and-animation.md' + - 'https://tanstack.com/table/latest/docs/guide/row-models' + - 'https://tanstack.com/db/latest/docs/guides/live-queries' + - 'https://tanstack.com/db/latest/docs/guides/mutations' + - 'https://tanstack.com/query/latest/docs/framework/react/guides/important-defaults' +--- + +# Coordinate Charts With TanStack + +Run **trigger → inspect → decide → build → verify** across the complete product state graph. Assign one owner to each row projection, semantic key, filter, selection, viewport, revision, transaction, and timing policy before connecting libraries. + +## Setup + +### Inspect the ownership graph + +Record these boundaries before writing adapters: + +| Concern | Normal owner | Chart input | +| --------------------------------------------------------- | ---------------------------------------------- | ----------------------------------------------------------------- | +| Remote fetching, cache, freshness, retry | Query | resolved semantic snapshot plus status | +| Normalized records, live queries, optimistic transactions | DB or sync collection | keyed projection plus sync state | +| Grid filtering, sorting, grouping, pagination | Table when grid-local; application when shared | explicitly chosen row-model frontier | +| Visual encoding, focus, scene, renderer | Charts | prepared rows and committed behavior options | +| Shared client intent | Store or application state | semantic filters, keys, and domains | +| Restorable navigation state | Router search | validated compact intent, never pixels or row objects | +| Mounted grid window | Virtual | no analytical effect unless “visible rows” is the stated question | +| Burst, queue, or batch policy | Pacer | paced source revisions or committed actions | +| Draft controls and validation | Form or local state | committed, valid intent | +| SSR request and hydration | Start or framework lifecycle | deterministic data revision, locale, and initial geometry | + +Fail condition: two libraries can independently change the same semantic state, or the chart receives rows without a named scope and revision. + +### Name each row projection + +Do not pass “the rows” between surfaces. Name the analytical frontier: + +- `sourceRows`: canonical records available to this client. +- `eligibleRows`: records after shared business filters. +- `summaryRows`: chart grain after aggregation or binning. +- `detailRows`: grid grain before presentation-only sorting or pagination. +- `visibleRows`: currently mounted virtual items; usually presentation only. +- `selectedRows`: rows resolved from stable semantic keys. + +Document whether grid sorting, filtering, grouping, pagination, or selection changes the chart question. Use the [Table and data-grid reference](references/table-and-data-grid.md) for row-model choices. + +## Core Patterns + +### Derive once, project deliberately + +Use one semantic projection owner, then expose separate summary and detail outputs: + +```ts +const analysis = projectOrders(sourceRows, sharedIntent) + +const tableData = analysis.detailRows +const chartDefinition = defineChart({ + marks: [ + lineY(analysis.summaryRows, { + x: 'day', + y: 'revenue', + color: 'region', + key: ({ datum }) => `${datum.region}:${datum.day.toISOString()}`, + }), + ], + x: { scale: scaleUtc }, + y: { scale: scaleLinear }, +}) +``` + +Do not independently restate eligibility, aggregation, or metric formulas in Table accessors and chart transforms. Presentation-only table sort and pagination may remain grid-owned. + +### Synchronize semantic intent, not component internals + +Share values such as: + +- entity IDs or aggregate bucket keys; +- validated filters and comparison periods; +- committed scale domains or time windows; +- selected metric and grouping dimension; +- data revision and “as of” status. + +Keep DOM nodes, row indexes, chart pixels, virtual indexes, tooltip bodies, and Table row objects inside their owning surface. + +### Separate transient, committed, and persisted state + +Use three phases where interaction can write data: + +1. **Transient**: hover, pointer position, drag preview, and active handle remain local and cheap. +2. **Committed**: selection, accepted brush domain, and submitted filters update shared application or URL state once. +3. **Persisted**: DB or server writes expose pending, confirmed, failed, and rollback states explicitly. + +Do not write every pointer frame to Router, Query, a sync engine, or a database transaction. Read [Query, DB, and sync](references/query-db-and-sync.md) when records are live or writable. + +### Pace at the expensive boundary + +Choose timing by semantics: + +- debounce draft search or filters that should wait for intent; +- throttle disposable visual previews that need bounded frequency; +- queue writes when every operation must persist; +- batch compatible mutations, telemetry, or source revisions; +- preserve latest-wins correctness for chart presentation even when writes are lossless. + +Do not add a second scheduler around work already bounded by the chart host, live-query engine, or framework. + +### Verify the full synchronization sequence + +1. Load one named data revision into both surfaces. +2. Apply a shared filter and prove summary totals reconcile with detail rows. +3. Sort and paginate the grid; prove the chart changes only when intended. +4. Select from chart and grid; prove both resolve the same semantic keys. +5. Apply an optimistic update; distinguish pending from confirmed output. +6. Roll back or receive a remote update; preserve surviving selection and clear missing keys. +7. Restore URL state, hydrate, resize, virtualize, and destroy without duplicate subscriptions or writes. + +## Common Mistakes + +### CRITICAL Charting the final rendered row model accidentally + +Wrong: feed the chart whatever rows the grid currently renders. + +Correct: choose the filtered, grouped, pre-pagination, page, or selected frontier because it matches the stated comparison. + +Table row models can include sorting, grouping, expansion, and pagination. The final grid row model is a presentation result, not an automatically valid analytical dataset. + +Source: TanStack Table row-model guide + +### CRITICAL Keying coordination by row or virtual index + +Wrong: synchronize `row.index`, array position, or virtual item index. + +Correct: define an application-level entity or aggregate key and adapt both Table and Charts to it. + +Indexes change under sorting, filtering, pagination, streaming, and virtualization. Grouped Table row IDs may also contain presentation suffixes. + +Source: TanStack Table rows guide; `API-FRICTION.md` F-120, F-131, and F-239 + +### CRITICAL Duplicating filtering and aggregation + +Wrong: implement the KPI in a Table aggregation function and again in chart preparation. + +Correct: derive one named semantic projection that can feed the grid, chart, table alternative, export, and tests. + +Independent pipelines drift on null policy, denominators, time boundaries, and later product changes. + +Source: `docs/guides/transforms-and-reactivity.md`; `API-FRICTION.md` F-128 and F-163 + +### CRITICAL Creating a bidirectional state loop + +Wrong: chart selection sets a Table filter whose change recreates chart selection and writes the URL again. + +Correct: assign one owner, normalize one event into semantic intent, and let every surface derive from that state without echoing equivalent updates. + +Feedback loops cause redundant renders, history spam, flicker, and state that cannot settle. + +Source: Charts interaction ownership; TanStack Router search-state guidance + +### HIGH Treating optimistic rows as confirmed history + +Wrong: animate an optimistic mutation into the ordinary historical series with no pending state. + +Correct: carry sync or transaction status into the semantic projection, differentiate pending values when decision-relevant, and verify rollback. + +TanStack DB applies optimistic state before persistence and can replace or roll it back after synchronization. + +Source: TanStack DB mutations and live-query guides + +### CRITICAL Persisting every interaction frame + +Wrong: write every brush, handle, resize, or pointer update through Router, Query invalidation, or a sync transaction. + +Correct: preview locally, pace expensive derivation if necessary, and commit one semantic value at the interaction boundary. + +Frame-rate state is presentation. Persistence needs validation, cancellation, ordering, and conflict semantics. + +Source: Charts interaction guides; TanStack Pacer timing guidance + +### HIGH Treating virtualization as analytical filtering + +Wrong: chart only mounted grid rows because Virtual currently exposes them. + +Correct: chart the intended source or filtered projection; use mounted rows only for an explicitly viewport-scoped question. + +Virtual controls render work, not dataset meaning. + +Source: TanStack Virtual virtualizer reference + +## References + +- [Table and data grids](references/table-and-data-grid.md) +- [Query, DB, and sync engines](references/query-db-and-sync.md) +- [TanStack application state and lifecycle](references/tanstack-application-state.md) + +See also: `prepare-chart-data/SKILL.md`, `build-chart-interactions/SKILL.md`, `update-and-animate-charts/SKILL.md`, and `debug-and-verify-charts/SKILL.md`. diff --git a/packages/charts-core/skills/coordinate-charts-with-tanstack/references/query-db-and-sync.md b/packages/charts-core/skills/coordinate-charts-with-tanstack/references/query-db-and-sync.md new file mode 100644 index 00000000..bd56460d --- /dev/null +++ b/packages/charts-core/skills/coordinate-charts-with-tanstack/references/query-db-and-sync.md @@ -0,0 +1,95 @@ +# Query, DB, and Sync Coordination + +Load this reference when chart rows come from TanStack Query, TanStack DB, a DB collection backed by a sync engine, or optimistic mutations. + +## Keep data responsibilities distinct + +| Layer | Owns | Must not silently own | +| ------------------- | --------------------------------------------------------------------- | ------------------------------------------------------------- | +| Query | request identity, fetching, cache, freshness, retry, cancellation | chart selection, visual domain, metric definition | +| DB collection | normalized keyed records, synchronization boundary, mutation handlers | chart-specific geometry or formatter state | +| DB live query | reactive filtering, joins, grouping, aggregation, projection | grid pagination or chart pixels | +| Sync engine | remote membership, replication, conflict and persistence contract | application interpretation of pending versus confirmed values | +| Semantic projection | metric grain, eligibility, units, lineage, revision envelope | remote retry or renderer lifecycle | +| Charts | scales, marks, scene, focus, rendering | network cache or database transaction state | + +Prefer one live projection that feeds both chart summaries and grid details when that query can preserve the required lineage. Use separate queries only when their scope is intentionally different and can be reconciled by revision. + +## Carry a revision envelope + +Do not expose only an array: + +```ts +interface ChartDataset { + rows: readonly Row[] + revision: string + asOf: Date + completeness: 'complete' | 'partial' | 'stale' + sync: 'confirmed' | 'mixed' | 'optimistic' +} +``` + +Adapt the fields to the actual backend contract. The point is to keep freshness and completeness out of incidental array identity. + +Use the same envelope for chart, grid, title, source note, export, and verification. Do not render a newly fetched chart beside stale grid details without making the mixed state explicit. + +## Respect live-query semantics + +TanStack DB live queries update as their source collections change and may maintain joins and aggregates incrementally. Let that layer own reusable relational work when it is the product's canonical client projection. + +Avoid: + +- copying a live-query result into a second hand-synchronized store; +- rebuilding the entire chart definition for unrelated collection changes; +- flattening a one-to-many join and then counting parent rows; +- using a live result's array order as entity identity; +- starting a tooltip query for every pointer candidate; +- subscribing separately per mark, cell, or tooltip body. + +Memoize chart preparation against the semantic result and options, not the top-level hook result object. + +## Present optimistic changes honestly + +DB mutations can apply locally before persistence, then become confirmed or roll back. Decide per metric whether optimistic values may enter: + +- totals and operational previews may include them with a pending indicator; +- audited, financial, or externally reported series may wait for confirmation; +- comparisons may show pending and confirmed values separately; +- animation must handle rollback and server-normalized values without changing entity keys. + +Do not label an optimistic aggregate “current confirmed revenue.” Do not celebrate success before the persistence contract completes. + +For draft editing, separate the preview transaction from commit. A chart can preview a local scenario without persisting each field or drag update. + +## Choose pressure semantics + +High-frequency input requires an explicit loss policy: + +| Work | Normal policy | Reason | +| ----------------------- | ----------------------------------------------- | -------------------------------------------- | +| Pointer or drag preview | throttle or animation-frame latest-wins | intermediate frames are disposable | +| Search/filter draft | debounce | wait for user intent | +| Live source revisions | coalesce or batch when revisions remain ordered | reduce repeated preparation | +| Database writes | queue or transactional batch | losing a write may corrupt state | +| Telemetry | bounded batch | preserve useful evidence without blocking UI | + +Never debounce a workflow that must preserve every mutation. Never queue unlimited visual frames that are already obsolete. + +## Verify synchronization + +- Initial load, background refetch, offline/stale, reconnect, and retry states. +- One-row live insert, update, delete, and join-key change. +- Optimistic apply, server normalization, persistence, conflict, and rollback. +- Shared chart/grid revision during partial sync and query switching. +- Stable keys and selections across array replacement and incremental updates. +- Bounded subscriptions, transformations, scene work, and teardown. +- No raw sensitive data in timing, transaction, or chart telemetry. + +Official references: + +- +- +- +- +- +- diff --git a/packages/charts-core/skills/coordinate-charts-with-tanstack/references/table-and-data-grid.md b/packages/charts-core/skills/coordinate-charts-with-tanstack/references/table-and-data-grid.md new file mode 100644 index 00000000..2b4b3bcc --- /dev/null +++ b/packages/charts-core/skills/coordinate-charts-with-tanstack/references/table-and-data-grid.md @@ -0,0 +1,90 @@ +# Table and Data-Grid Coordination + +Load this reference when a chart and TanStack Table or another data grid share rows, filters, grouping, selection, or drill-down. + +## Choose the analytical frontier + +| Desired chart meaning | Grid frontier to consider | Warning | +| ----------------------------------- | ------------------------------------------ | -------------------------------------------------------------------------- | +| All records available to the client | core/source data | May still be only a server page or partial sync | +| Records matching shared filters | filtered or application-owned projection | Keep grid-only text filters separate when they should not change the chart | +| Group summaries | shared semantic aggregation | Table grouping rows may contain presentation IDs and nested subrows | +| Current page | paginated rows | Use only when the title says the chart covers the current page | +| Selected records | semantic keys resolved against source data | Selected-row models may only resolve rows currently available to the table | +| Currently mounted rows | never by default | Virtualization is a rendering window, not a data filter | + +Name the chosen frontier in code and in tests. “Use the table rows” is incomplete. + +## Coordinate filters + +Classify each filter: + +- **Business filter** changes metric eligibility and should normally affect chart, grid, export, and summary. +- **Presentation filter** helps find a detail row and may remain grid-local. +- **Cross-filter** originates from a visual selection and deliberately changes another view. +- **Highlight** changes emphasis but not eligibility or totals. + +Do not turn every chart selection into a filter. Highlighting preserves context; filtering removes it. + +Verify that clearing a filter restores the same semantic revision and that empty results distinguish “no eligible data” from “not loaded.” + +## Coordinate selection + +Use an application key independent of either library: + +```ts +type EntityKey = string + +interface CoordinatedSelection { + keys: ReadonlySet + source: 'chart' | 'grid' | 'url' | 'application' +} +``` + +- Configure the table row ID from the entity key. +- Configure chart datum keys from the entity or aggregate key. +- Resolve grouped chart buckets to contributing entity keys only when drill-down requires it. +- Preserve selection when keys survive sorting, filtering, pagination, and live updates. +- Clear or explain keys that are no longer eligible or authorized. + +Do not share Table row objects, ChartPoint objects, row indexes, or DOM references. + +## Keep values semantic + +Table accessors may return formatted strings, cached cell values, aggregates, or presentation metadata. Feed Charts the underlying numeric, temporal, categorical, or interval values. + +Common errors: + +- plotting a formatted currency string; +- sorting a time-series path by the current grid column; +- using an expanded group row and its children as independent observations; +- counting duplicated join rows as distinct entities; +- charting only a client page while labeling it as the full result; +- applying server pagination with client-only sorting or filtering and implying global order. + +## Use chart and grid as complements + +Prefer these product patterns: + +- chart summary plus grid detail from one semantic projection; +- chart selection highlights matching grid rows without removing context; +- grid selection annotates or compares a bounded subset in the chart; +- chart brush commits a semantic time range used by both surfaces; +- grid expansion reveals lineage for one chart aggregate; +- chart and accessible data table share metric formatting and source notes. + +## Verify reconciliation + +- Aggregate detail rows independently and compare them with chart summaries. +- Test pre-filtered, filtered, grouped, pre-pagination, paginated, and selected scopes. +- Sort by an unrelated column and prove time-series topology remains correct. +- Paginate and virtualize without losing offscreen selection. +- Apply remote inserts, updates, and deletes while a row is selected. +- Test duplicate source entities and grouped rows with derived IDs. + +Official references: + +- +- +- +- diff --git a/packages/charts-core/skills/coordinate-charts-with-tanstack/references/tanstack-application-state.md b/packages/charts-core/skills/coordinate-charts-with-tanstack/references/tanstack-application-state.md new file mode 100644 index 00000000..9dc2f8eb --- /dev/null +++ b/packages/charts-core/skills/coordinate-charts-with-tanstack/references/tanstack-application-state.md @@ -0,0 +1,95 @@ +# TanStack Application State and Lifecycle + +Load this reference when Charts coordinates with Router, Store, Virtual, Pacer, Start, Form, or other TanStack application infrastructure. + +## Route state by lifetime + +| State | Owner | Examples | +| ------------------------------- | --------------------------------- | ------------------------------------------------------------------ | +| Ephemeral presentation | Charts or component | hover, pointer coordinate, open tooltip, drag preview | +| Shared session intent | Store or lifted application state | selected IDs, active metric, linked-view domain | +| Restorable navigation | Router search | validated filters, comparison period, grouping, committed viewport | +| Server state | Query | fetched records, freshness, errors, retries | +| Synchronized records and writes | DB | live collections, optimistic transactions, persistence | +| Draft input | Form or local state | uncommitted thresholds, date input, scenario parameters | + +Promote state only when another consumer or lifecycle needs it. Do not mirror the same value in Chart behavior, Table state, Store, and Router. + +## Router + +Put compact, semantic, validated intent in search parameters: + +- metric and grouping identifiers; +- filter values; +- selected stable IDs when safe to share; +- committed time domain; +- comparison mode. + +Do not serialize pixels, DOM state, tooltip content, entire row objects, huge selections, unvalidated expressions, or sensitive values. Replace high-frequency intermediate navigation rather than adding history entries, and commit a deliberate state when the interaction ends. + +Verify back/forward navigation, invalid URLs, schema migrations, permissions, and SSR hydration. + +## Store + +Use Store for shared client state that is neither server data nor navigation state. Keep subscriptions narrow and derive view-specific values instead of hand-synchronizing copies. + +Good candidates: + +- selected semantic keys shared by chart and grid; +- linked crosshair value across coordinated views; +- dashboard comparison mode; +- transient draft state shared across sibling controls. + +Keep high-frequency state local unless another mounted surface truly consumes it. Normalize equivalent updates before publishing them to prevent echo loops. + +## Virtual + +Virtual owns which grid rows or dashboard lanes are mounted and their scroll measurements. It does not redefine the data population. + +- Use source or filtered rows for an overall chart. +- Use the virtual window only for a chart explicitly summarizing visible rows. +- Keep selected offscreen keys in semantic state. +- Coordinate scroll-to-row by stable key, resolving the current index only at the Virtual boundary. +- Do not create one active chart runtime for every unmounted dashboard lane. + +## Pacer + +Pace the expensive boundary with the correct loss semantics: + +- debounce draft filters and scenario inputs; +- throttle disposable previews; +- queue required writes; +- batch compatible changes and telemetry; +- flush or cancel on commit, navigation, and teardown as appropriate. + +Surface pending or queued state when it affects user expectations. Do not stack unrelated debounce policies across Form, Router, Query, DB, and chart preparation. + +## Start and SSR + +Resolve one deterministic request snapshot for chart and grid. Keep locale, time zone, filter schema, data revision, and initial chart geometry consistent across server render and hydration. + +Protect private data at the server function or route that serves it; route UI guards are not the authorization boundary. Avoid streaming partial chart and grid states that appear directly comparable unless completeness is visible. + +## Form + +Use Form or local draft state when chart controls require validation, linked fields, cancellation, or an explicit apply action. Preview valid draft values locally, then commit one semantic intent or DB transaction. + +Do not let an invalid date range, logarithmic bound, denominator, or scenario parameter reach chart scales or synchronized state merely because a field changed. + +## Verify the product workflow + +- Restore a deep link and reproduce the same metric, filters, revision, and domain. +- Traverse chart, grid, and controls by keyboard without competing shortcuts. +- Scroll and virtualize without changing analytical totals. +- Type quickly into filters and prove obsolete requests and projections cannot win. +- Commit, cancel, retry, navigate, and destroy with no pending timer or subscription leak. +- Hydrate with the same locale, time zone, data status, and geometry used by the server. + +Official references: + +- +- +- +- +- +- diff --git a/packages/charts-core/skills/debug-and-verify-charts/SKILL.md b/packages/charts-core/skills/debug-and-verify-charts/SKILL.md new file mode 100644 index 00000000..d13f2f67 --- /dev/null +++ b/packages/charts-core/skills/debug-and-verify-charts/SKILL.md @@ -0,0 +1,163 @@ +--- +name: debug-and-verify-charts +description: > + Diagnose TanStack Charts type, data, scale, geometry, focus, lifecycle, + bundle, and performance failures with scenario-level evidence. Load for + blank or clipped charts, misleading output, unsafe casts, migration parity, + packed-package checks, or regressions. +metadata: + type: lifecycle + library: '@tanstack/charts' + library_version: '0.9.0' +sources: + - 'TanStack/charts:docs/guides/testing-and-debugging.md' + - 'TanStack/charts:docs/guides/typescript.md' + - 'TanStack/charts:docs/guides/bundle-size-and-performance.md' + - 'TanStack/charts:API-FRICTION.md' +--- + +# Debug and Verify Charts + +Run **trigger → inspect → decide → build → verify** at the narrowest layer that owns the failure. Do not change rendering before proving prepared rows, channels, scales, and scene geometry are correct. + +## Semantic and Scene Checks + +### Check: deterministic scene invariants + +Expected: + +```ts +import { + createChartScene, + defineChart, + lineY, + renderChartSvg, +} from '@tanstack/charts' +import { scaleLinear } from '@tanstack/charts/scales/linear' +import { scalePoint } from '@tanstack/charts/scales/point' + +const rows = [ + { month: 'Jan', value: 42 }, + { month: 'Feb', value: 58 }, + { month: 'Mar', value: 76 }, +] + +const definition = defineChart({ + marks: [lineY(rows, { x: 'month', y: 'value', points: true })], + x: { scale: scalePoint }, + y: { scale: scaleLinear }, +}) + +export const scene = createChartScene(definition, { width: 640, height: 360 }) +export const svg = renderChartSvg(scene, { ariaLabel: 'Monthly value' }) + +if (scene.points.length !== rows.length) throw new Error('Point count mismatch') +if (scene.chart.width <= 0) throw new Error('Empty plot width') +if (/NaN|Infinity/.test(svg)) throw new Error('Invalid numeric SVG output') +``` + +Fail condition: expected points/domains are absent, geometry is non-finite, or final bounds are empty. + +Fix: diagnose in the layer order below before mounting a host. + +### Check: diagnose in ownership order + +1. Prepared rows, order, intervals, missing values, and units. +2. Materialized mark channels and grouping. +3. Resolved scale domains, ranges, bandwidths, ticks, and log validity. +4. `scene.chart` bounds and guide margins. +5. Scene node keys, geometry, clipping, and interaction points. +6. Renderer input and serialized/static output. +7. Mounted surface, framework lifecycle, fonts, and application CSS. + +If the scene is wrong, changing the renderer cannot repair it. If the scene is correct, stop changing transforms and scales. + +## Interaction and Lifecycle Checks + +### Check: verify a user sequence + +1. Focus a known datum by pointer. +2. Verify primary/grouped semantic points and tooltip content. +3. Pin or select it. +4. Reorder, filter, update, resize, or interrupt motion. +5. Verify state follows a surviving key and clears when the key disappears. +6. Traverse and activate the same information by keyboard. +7. Destroy the host and verify no observer, event, tooltip, or frame survives. + +### Check: pair screenshots with semantic assertions + +Use screenshots for label collision, font metrics, gradients, clipping, crossings, themes, and dense aliasing. Fix data, fonts, viewport, device scale, animation, locale, time zone, and revision. Pair the image with geometry and state assertions. + +## Package and Performance Checks + +### Check: test a packed consumer + +Install the produced tarball in a minimal consumer. Verify documented root and exact subpath imports, SSR evaluation, ESM tree shaking, optional peer boundaries, retained modules, declaration output, and runtime behavior. + +### Check: performance samples have correctness gates + +Every accepted sample must prove the requested revision painted, representative geometry is finite/in-bounds, expected points exist, no lifecycle error occurred, and interaction belongs to the latest scene. Report environment, data/scene size, warmup, sample count, percentiles, and capability set. + +## Common Mistakes + +### CRITICAL Casting a rejected definition + +Wrong: + +```tsx + +``` + +Correct: repair the row type, channel, scale domain, mixed mark union, host-specific tooltip type, or custom-mark contract at its source. + +A cast hides the mismatch and leaves runtime semantics dishonest. + +Source: `docs/guides/typescript.md`; `docs/reference/types.md` + +### CRITICAL Asserting only element existence + +Wrong: assert that an SVG, path, or canvas exists. + +Correct: assert semantic domains, final bounds, geometry, resolved datum/group, update sequence, accessibility, and teardown at the layer that owns each behavior. + +Presence does not prove correctness. + +Source: `API-FRICTION.md` F-036, F-073, F-081; `docs/guides/testing-and-debugging.md` + +### HIGH Testing workspace source instead of packed exports + +Wrong: approve because monorepo tests resolve source aliases. + +Correct: install the packed package into a consumer and test public exports, peers, declarations, SSR, and bundles there. + +Workspace resolution can hide missing exports, browser globals, retained optional dependencies, and publish-file omissions. + +Source: `API-FRICTION.md` F-090, F-139, F-167, F-224 + +### HIGH Trusting one aggregate performance number + +Wrong: report one mount median for one chart. + +Correct: separate preparation, scene build, paint, sustained pointer work, updates, dashboard lifecycle, correctness gates, and bundle capability. + +An aggregate can hide stale output, invalid attempts, or a cost shifted into another layer. + +Source: `API-FRICTION.md` F-078–F-084; `docs/guides/bundle-size-and-performance.md` + +### HIGH Tension: motion continuity versus current-state correctness + +Final-frame checks miss stale focus, guides, geometry, or latest-wins violations during interruption. Test the state transition while it is happening. + +See also: `update-and-animate-charts/SKILL.md` and `design-responsive-charts/SKILL.md` + +## Release Summary + +- [ ] Types pass without casts or suppressed inference. +- [ ] Empty, missing, duplicate, negative, and invalid data cases pass. +- [ ] Scene semantics and geometry pass at narrow and wide sizes. +- [ ] Pointer, keyboard, selection, update, interruption, and teardown pass. +- [ ] SSR/static output and accessibility pass. +- [ ] Packed consumer exports and bundles pass. +- [ ] Performance samples include correctness gates and target workload shape. + +See also: `coordinate-charts-with-tanstack/SKILL.md` — chart-grid reconciliation, URL restoration, sync status, optimistic rollback, and virtualized teardown require product-level sequences. diff --git a/packages/charts-core/skills/design-a-chart/SKILL.md b/packages/charts-core/skills/design-a-chart/SKILL.md new file mode 100644 index 00000000..bb757376 --- /dev/null +++ b/packages/charts-core/skills/design-a-chart/SKILL.md @@ -0,0 +1,154 @@ +--- +name: design-a-chart +description: > + Choose an honest visualization from a user story, metric, comparison, + projection, target, or explanatory goal before selecting TanStack Charts + marks. Load whenever a user asks to graph, chart, visualize, compare, + forecast, project, explain, or communicate data. +metadata: + type: core + library: '@tanstack/charts' + library_version: '0.9.0' +sources: + - 'TanStack/charts:docs/guides/choosing-a-chart.md' + - 'TanStack/charts:docs/examples/*.md' + - 'TanStack/charts:docs/reference/transforms.md' +--- + +# Design a Chart From a User Goal + +Use this scenario loop: **trigger → inspect → decide → build → verify**. Do not begin with a chart type, even when the request names one. + +## Setup + +Turn the request into this brief before writing chart code: + +```ts +interface ChartBrief { + question: string + decision: string + observation: string + metric: { value: string; unit: string; denominator?: string } + comparison: + 'time' | 'category' | 'distribution' | 'relationship' | 'composition' + evidence: readonly string[] +} + +export const brief: ChartBrief = { + question: + 'Which acquisition channel improved conversion without losing volume?', + decision: 'Choose where to increase next-month spend', + observation: 'one row per channel and month', + metric: { value: 'conversionRate', unit: '%', denominator: 'sessions' }, + comparison: 'time', + evidence: ['conversion rate', 'sessions', 'month', 'channel'], +} +``` + +If the question, observation, unit, denominator, or decision is unknown, inspect the data and surrounding product before choosing marks. + +## Core Patterns + +### Match the form to the reader's comparison + +- Change over ordered time → line; discrete periods → bars. +- Named-category magnitude → sorted horizontal bars or dots. +- Distribution → histogram, ECDF, box, violin, or faceted histograms. +- Relationship → scatterplot; add size only for a meaningful third quantity. +- Composition → stack for totals, normalized stack for proportions, mosaic for two categorical dimensions. +- Flow, hierarchy, network, or spatial questions → use their first-party layouts only when topology is the question. + +Read [the visual-task matrix](references/visual-task-matrix.md) for the full routing table. + +### Separate observed, target, and projected values + +```ts +import { areaY, defineChart, lineY, ruleY } from '@tanstack/charts' +import { scaleLinear } from '@tanstack/charts/scales/linear' +import { scalePoint } from '@tanstack/charts/scales/point' + +const rows = [ + { month: 'Jan', actual: 82, forecast: null, low: null, high: null }, + { month: 'Feb', actual: 91, forecast: null, low: null, high: null }, + { month: 'Mar', actual: null, forecast: 96, low: 88, high: 106 }, + { month: 'Apr', actual: null, forecast: 103, low: 90, high: 119 }, +] + +export const chart = defineChart({ + marks: [ + areaY(rows, { x: 'month', y1: 'low', y2: 'high', fillOpacity: 0.15 }), + lineY(rows, { x: 'month', y: 'actual', strokeWidth: 2.5 }), + lineY(rows, { x: 'month', y: 'forecast', strokeDasharray: '5 4' }), + ruleY([100], { strokeDasharray: '2 3' }), + ], + x: { scale: scalePoint }, + y: { scale: scaleLinear, axis: { label: 'Indexed revenue' } }, +}) +``` + +Use different channels for status and uncertainty. A continuous unqualified line implies equal epistemic status. + +### Define proof before polish + +For every chart, verify: + +- the visual answers the stated question; +- axes, legend, title, or adjacent copy identify units and comparison; +- ordering, aggregation, missing-value policy, and baseline are deliberate; +- exact-value tasks have a table or textual equivalent; +- the smallest supported container preserves the important comparison; +- pointer, keyboard, updates, and empty states tell the same story. + +## Common Mistakes + +### CRITICAL Starting with the requested chart type + +Wrong: implement “make this a pie chart” before identifying the comparison. + +Correct: restate the decision and recommend the form that makes that comparison perceptually direct. If the user retains a weaker form, state its analytical limitation and preserve the underlying semantics. + +A familiar chart can answer a different question than the user needs. + +Source: `docs/guides/choosing-a-chart.md` + +### HIGH Showing a rate without its denominator + +Wrong: show conversion rate alone. + +Correct: keep sessions or eligible population in the prepared row and expose it beside the rate or in the tooltip. + +Normalized values can reverse interpretation when volume changes. + +Source: `API-FRICTION.md` F-217; `docs/reference/transforms.md` + +### CRITICAL Rendering projections as observed history + +Wrong: connect actuals and forecasts with one undifferentiated line. + +Correct: encode the forecast boundary, projected segment, and uncertainty explicitly. + +Continuous treatment implies equal certainty. + +Source: `docs/examples/lines-and-areas.md`; `docs/reference/marks/difference.md` + +### HIGH Choosing area or angle for precise ranking + +Wrong: rank close values with wedges, bubbles, or interior stack layers. + +Correct: use aligned position or length when exact ordering is the reader's task. + +Area and angle emphasize shape or part-to-whole relationships, not precise rank. + +Source: `docs/guides/choosing-a-chart.md`; `docs/examples/bars-and-rankings.md` + +### HIGH Tension: analytical honesty versus visual simplicity + +Simplifying aggregation can hide denominators, lineage, uncertainty, or missing-value policy. Preserve the evidence needed to interpret the result before reducing visual detail. + +See also: `prepare-chart-data/SKILL.md` § Common Mistakes + +## References + +- [Analytical task and visual-form matrix](references/visual-task-matrix.md) + +See also: `prepare-chart-data/SKILL.md` and `compose-marks-and-views/SKILL.md` — the analytical task determines both the transform and mark composition. diff --git a/packages/charts-core/skills/design-a-chart/references/visual-task-matrix.md b/packages/charts-core/skills/design-a-chart/references/visual-task-matrix.md new file mode 100644 index 00000000..46447976 --- /dev/null +++ b/packages/charts-core/skills/design-a-chart/references/visual-task-matrix.md @@ -0,0 +1,54 @@ +# Visual Task Matrix + +Use the first row that matches the reader's decision. Then inspect row grain, units, denominators, missing values, ordering, and uncertainty before choosing TanStack Charts primitives. + +| Reader task | Data requirement | First form | TanStack Charts strategy | Verify | Avoid | +| -------------------------------- | ----------------------------------------------------- | ---------------------------------------------------- | ------------------------------------------------------------------ | ------------------------------------------- | ----------------------------------------------- | +| Follow change | Ordered temporal or ordinal x; one or more quantities | Line | `lineY`; add `areaY` only for meaningful magnitude or interval | order, gaps, series identity, time grain | connecting unordered categories | +| Compare discrete periods | Ordered periods with independent totals | Bars | `barY` or `barX`; preserve zero when length carries magnitude | baseline, period completeness | line continuity when periods are not continuous | +| Rank categories | Named categories and one quantity | Sorted horizontal bars or dots | sort rows before `barX` or `dot` | ties, zero, negative values, visible labels | wedges or bubbles for close values | +| Compare two values per category | Two measures with the same unit and category grain | Dumbbell or slopegraph | `dot` plus `link`/`lineY`; grouped bars when absolute zero matters | direction, common scale | dual axes | +| Show progress to target | Actual, target, and directionality | Bar/dot plus target rule | primary mark plus `ruleX`/`ruleY`; annotate variance | target period and unit | gauge decoration without scale context | +| Explain a KPI change | Current value plus contributing categories or stages | Ranked contribution bars, waterfall, or before/after | `waterfall`, `barX`, `link`, annotations | contributions reconcile to total | unexplained single headline number | +| Inspect a relationship | Two quantitative variables; optional group | Scatterplot | `dot`; use regression or density only when it answers the question | scale shape, outliers, overplotting | implying causation | +| Show a third quantity | Two positions plus non-negative magnitude | Bubble scatterplot | `dot` with an area-preserving radius scale | radius represents area; legend exists | mapping signed values to radius | +| Inspect one distribution | Raw observations or honest bins | Histogram or ECDF | `binX` plus bars; cumulative transform plus line | bin sensitivity, sample size | only mean and standard deviation | +| Compare distributions | Raw observations by group | Box, violin, ridgeline, or facets | first-party composite marks; retain source lineage | sample size, outliers, common scale | overlapping filled densities without legibility | +| Show uncertainty | Estimate with lower/upper endpoints | Interval, error bar, range area | `areaY`, ranged rect/bar, links/rules | interval meaning and confidence level | treating interval endpoints as separate series | +| Show observed plus forecast | Ordered history, boundary, projection, uncertainty | Solid history, differentiated forecast, interval | layered `lineY`, `areaY`, boundary annotation | status remains clear without color | one continuous unqualified line | +| Show composition over categories | Components and total | Stacked bars | `barY` with `stack()` or inferred stack | totals, negative policy, layer order | comparing interior layers precisely | +| Show changing composition | Ordered x, component values | Stacked area or normalized stack | `areaY` with stacking/normalization | missing series and denominator | interpreting proportions as volume | +| Show two categorical dimensions | Counts or values by two categories | Mosaic or heatmap | mosaic transform/mark or `rect` cells | marginal totals and empty cells | arbitrary area without labels | +| Show a matrix | Two categorical/ordinal dimensions and cell value | Heatmap | `rect` with x/y intervals or bands and color scale | color domain, missing versus zero | rainbow scales for ordered values | +| Repeat a comparison | Same question across groups | Facets | facet or named views sharing semantic domains | panel order, shared domains, smallest panel | one legend or axis whose scope is unclear | +| Show topology | Nodes and edges where connection is the question | Node-link or adjacency matrix | force/link primitives or prepared matrix | disconnected nodes, direction, edge meaning | node-link for very dense graphs | +| Show flow | Weighted source-target stages | Sankey | first-party Sankey layout | conservation, cycles, dropped nodes | using width when values do not reconcile | +| Show hierarchy | Parent-child structure and quantity | Treemap, sunburst, or tree | first-party hierarchy primitives | root policy, negative values, label fit | implying area precision for small leaves | +| Show geography | Coordinates or regions tied to a spatial question | Map | `geo` primitives with explicit projection | projection, missing regions, spatial unit | maps when rank is the actual task | +| Show density | Many points where individual identity is not required | Hexbin, contour, or raster density | first-party spatial layouts using final bounds | bin/bandwidth sensitivity | raw points that saturate the surface | +| Navigate or edit a range | Ordered scale and accepted application state | Chart plus semantic control | `brushX`, `zoomX`, `handleX`, or application control | keyboard path, cancellation, clamping | pointer-only transparent overlays | + +## Metric Checks + +| Metric kind | Carry with it | Failure to prevent | +| ------------------ | --------------------------------------- | ------------------------------------------- | +| Count | eligibility and time window | comparing unequal exposure | +| Rate or percentage | numerator and denominator | small-base volatility and Simpson's paradox | +| Average | sample count and distribution shape | hiding skew or mixture changes | +| Index | base period and formula | treating an index as an absolute unit | +| Currency | currency, real/nominal basis, period | mixing units or inflation bases | +| Duration | start/stop rules and censoring | treating incomplete intervals as zero | +| Forecast | cutoff, horizon, model status, interval | presenting estimates as observations | +| Target | owner, period, direction, reset policy | comparing actuals to a stale target | +| Cumulative value | reset boundary and flow/stock meaning | interpreting a stock as period change | + +## Recommendation Policy + +When the requested form conflicts with the task: + +1. State the mismatch in one sentence. +2. Recommend the form that supports the decision. +3. Preserve the user's requested form only if its semantics remain honest. +4. Make any perceptual limitation explicit in the chart or surrounding explanation. + +Source: `docs/guides/choosing-a-chart.md` diff --git a/packages/charts-core/skills/design-responsive-charts/SKILL.md b/packages/charts-core/skills/design-responsive-charts/SKILL.md new file mode 100644 index 00000000..e200c94a --- /dev/null +++ b/packages/charts-core/skills/design-responsive-charts/SKILL.md @@ -0,0 +1,206 @@ +--- +name: design-responsive-charts +description: > + Make TanStack Charts adapt to measured containers, information priority, + guide margins, SSR initial width, and final plot bounds. Load for narrow + dashboards, overflow, label density, responsive topology, or pixel-space + layouts. +metadata: + type: core + library: '@tanstack/charts' + library_version: '0.9.0' +sources: + - 'TanStack/charts:docs/guides/responsive-charts.md' + - 'TanStack/charts:docs/reference/chart-definitions.md' + - 'TanStack/charts:docs/reference/marks/waffle.md' + - 'TanStack/charts:docs/reference/marks/treemap.md' +--- + +# Design Responsive Charts + +Use **trigger → inspect → decide → build → verify**. Adapt to the chart container and information priority; do not equate responsive design with stretching geometry. + +## Setup + +Omit host width, choose height deliberately, and use the responsive definition only for surface-dependent decisions: + +```ts +import { barX, defineChart, mountChart } from '@tanstack/charts' +import { scaleBand } from '@tanstack/charts/scales/band' +import { scaleLinear } from '@tanstack/charts/scales/linear' + +const rows = [ + { feature: 'Account recovery', requests: 128 }, + { feature: 'Saved searches', requests: 95 }, + { feature: 'Audit export', requests: 72 }, +] + +const definition = defineChart(({ width }) => ({ + marks: [barX(rows, { x: 'requests', y: 'feature' })], + x: { + scale: scaleLinear, + nice: true, + axis: { ticks: { count: width < 420 ? 3 : 6 } }, + }, + y: { scale: () => scaleBand().padding(0.1) }, +})) + +const element = document.querySelector('#feature-chart') +if (!element) throw new Error('Missing #feature-chart') + +export const host = mountChart(element, { + definition, + height: 320, + initialWidth: 640, + ariaLabel: 'Weekly feature requests', +}) +``` + +The container's grid or flex item must be allowed to shrink, usually with `min-width: 0`. + +## Core Patterns + +### Adapt information in priority order + +At narrower containers: + +1. Reduce tick candidates and optional annotations. +2. Thin, abbreviate, rotate, or directly label essential values. +3. Change orientation or facet layout without changing metric semantics. +4. Aggregate only when the question remains valid. +5. Replace the chart with a focused summary or accessible table when the comparison no longer fits. + +Keep domains, thresholds, units, and category-color assignments stable when readers compare the same chart across sizes. + +### Distinguish surface bounds from plot bounds + +The responsive builder receives full surface `width` and `height`. Axes, legends, and measured text later determine `scene.chart`, the final inner plot. Use: + +- responsive builder context for surface breakpoints and representation choice; +- custom-mark render bounds for plot-space geometry; +- `host.getScene().chart` for application overlays after render. + +Do not duplicate margin math in application code. + +### Treat topology as responsive state + +Waffle packing, treemaps, Sankey columns, facets, Delaunay links, hexbins, density contours, and label fit can change membership or arrangement when final bounds change. Verify semantic keys and interaction state after topology changes, not only node dimensions. + +### Use deterministic initial geometry + +Supply the same `initialWidth` for equivalent server renders. The client adopts the measured container width after hydration. Use fixed `width` only for exports, benchmarks, or another application-owned frame. + +## Common Mistakes + +### CRITICAL Adapting to viewport width + +Wrong: + +```ts +const compact = window.innerWidth < 640 +``` + +Correct: + +```ts +defineChart(({ width }) => ({ + marks, + x: { + scale: scaleLinear, + axis: { ticks: { count: width < 420 ? 4 : 8 } }, + }, +})) +``` + +Dashboard panels and embeds can be narrow inside a wide viewport. + +Source: `docs/guides/responsive-charts.md` + +### CRITICAL Using surface width as plot width + +Wrong: compute bins, collisions, or overlay positions directly from responsive builder width. + +Correct: perform exact pixel work from resolved custom-mark bounds or `scene.chart`. + +Guides and legends reduce the final plot after the builder returns. + +Source: `API-FRICTION.md` F-116, F-219; `docs/guides/responsive-charts.md` + +### HIGH Assuming resize only stretches geometry + +Wrong: preserve old treemap, facet, waffle, Delaunay, hexbin, or label topology and scale its pixels. + +Correct: let the owning resolved-layout primitive recompute from final bounds and preserve semantic keys through the change. + +Many final-pixel layouts change topology, not only size. + +Source: waffle, treemap, Delaunay, and hexbin mark references + +### HIGH Using fixed width for application charts + +Wrong: + +```ts +mountChart(element, { definition, width: 640, height: 320, ariaLabel }) +``` + +Correct: + +```ts +mountChart(element, { + definition, + height: 320, + initialWidth: 640, + ariaLabel, +}) +``` + +Fixed width opts out of container measurement; missing `initialWidth` or shrink constraints causes different SSR or overflow failures. + +Source: `docs/guides/responsive-charts.md`; `API-FRICTION.md` F-111 + +### CRITICAL Assigning positional pixel ranges + +Wrong: + +```ts +x: { + scale: scaleLinear().range([0, 640]) +} +``` + +Correct: + +```ts +x: { + scale: scaleLinear +} +``` + +The range must follow the final plot after container measurement and guide margins resolve. + +Source: `API-FRICTION.md` F-002; `docs/concepts/scales-and-d3.md` + +### HIGH Treating containment as collision avoidance + +Wrong: expect automatic margins to make every long tick label readable. + +Correct: choose responsive candidate spacing, thinning, rotation, abbreviation, or a different composition. + +Margins contain guides inside the surface; they do not resolve every label-label collision. + +Source: `API-FRICTION.md` F-023, F-160; `docs/guides/responsive-charts.md` + +### HIGH Tension: responsive adaptation versus comparison stability + +Reduce guide density and change composition without silently changing what position, color, or a threshold means. + +See also: `configure-scales-guides-color/SKILL.md` § Common Mistakes + +### HIGH Tension: motion continuity versus current-state correctness + +Responsive relayout should commit immediately by default. Animating every observed resize can leave geometry behind the actual panel size and repeatedly interrupt transitions. + +See also: `update-and-animate-charts/SKILL.md` and `debug-and-verify-charts/SKILL.md` + +See also: `ship-accessible-charts/SKILL.md` and `update-and-animate-charts/SKILL.md` — initial size, label priority, and resize policy affect hydration, accessibility, and motion. diff --git a/packages/charts-core/skills/extend-tanstack-charts/SKILL.md b/packages/charts-core/skills/extend-tanstack-charts/SKILL.md new file mode 100644 index 00000000..a479da2d --- /dev/null +++ b/packages/charts-core/skills/extend-tanstack-charts/SKILL.md @@ -0,0 +1,158 @@ +--- +name: extend-tanstack-charts +description: > + Implement TanStack Charts custom marks, scale-value contracts, + final-bounds layouts, renderer-neutral scene nodes, renderers, hosts, + controls, or composed views after built-in primitives are exhausted. +metadata: + type: core + library: '@tanstack/charts' + library_version: '0.9.0' +requires: + - compose-marks-and-views +sources: + - 'TanStack/charts:docs/guides/custom-marks-and-renderers.md' + - 'TanStack/charts:docs/reference/custom-extensions.md' + - 'TanStack/charts:packages/charts-core/src/mark.ts' + - 'TanStack/charts:packages/charts-core/src/mark-with-scale-values.ts' +--- + +# Extend TanStack Charts + +Use **trigger → inspect → decide → build → verify**. Extend the narrowest ownership boundary after proving built-in marks, first-party layouts, transforms, facets, views, and controlled behaviors cannot express the required semantics. + +## Setup + +Create a renderer-neutral mark with declared scale values and deterministic scene keys: + +```ts +import { createMark, defineChart } from '@tanstack/charts' +import { scaleLinear } from '@tanstack/charts/scales/linear' + +interface ThresholdDatum { + id: string + value: number +} + +const threshold = createMark(({ markIndex }) => { + const datum: ThresholdDatum = { id: 'target', value: 75 } + + return { + id: `threshold-${markIndex}`, + channels: { y: { scale: 'y', values: [datum.value] } }, + render({ chart, scales, theme }) { + const y = scales.y.map(datum.value) + return { + nodes: [ + { + kind: 'rule', + key: datum.id, + x1: chart.x, + x2: chart.x + chart.width, + y1: y, + y2: y, + style: { stroke: theme.foreground, strokeOpacity: 0.55 }, + }, + ], + } + }, + } +}) + +export const chart = defineChart({ + marks: [threshold], + y: { scale: scaleLinear }, +}) +``` + +This mark is decorative, so it emits no fake interaction point. + +## Core Patterns + +### Escalate through extension boundaries + +1. Built-in or first-party composite mark. +2. Several built-in marks. +3. Facet or named view composition. +4. D3/application-prepared semantic rows. +5. `compositeMark` for a reusable group of ordinary marks. +6. `createMark` for new renderer-neutral geometry. +7. `resolveLayout` only for final-bounds topology or collision. +8. Custom control for reusable semantic behavior. +9. Custom renderer/host for a different platform surface. + +Read [the extension protocol matrix](references/extension-protocols.md) before choosing a boundary. + +### Materialize values before rendering + +Initialization declares every semantic value that must establish x, y, or color domains. Rendering maps those values through resolved scales. Never infer a private positional domain inside `render`. + +### Emit honest interaction points + +Only emit points for semantic targets. Each point keeps the original datum, stable key, semantic values, resolved coordinates, and group/color identity. Attach the same point object to the scene primitive it paints. Use focus anchors for reveal-only geometry and focus guides for data-less cursor presentation. + +### Use final-layout callbacks only for final-layout work + +Use `resolveLayout` for binning, collision, packing, or topology that depends on resolved scales and inner bounds. Keep semantic row transforms eager and outside render. Keep layout callbacks synchronous, pure, and deterministic because margin solving can call them more than once. + +## Common Mistakes + +### CRITICAL Reading or mutating the DOM during scene generation + +Wrong: query text, append SVG, or inspect browser layout in `initialize`, `resolveLayout`, or `render`. + +Correct: consume the supplied bounds, scales, theme, text layout, and scene contracts; put platform lifecycle in a renderer or host extension. + +Scene compilation must remain deterministic for SSR, Canvas, native, export, and tests. + +Source: `docs/guides/custom-marks-and-renderers.md`; `packages/charts-core/src/mark.ts` + +### CRITICAL Inferring a private positional domain in render + +Wrong: derive a local domain and scale after chart scales have resolved. + +Correct: materialize positional channel values during initialization, then map with `context.scales`. + +Private domains prevent coordinated guides, layers, focus, and views. + +Source: `docs/reference/custom-extensions.md`; archived custom-mark notes + +### HIGH Conflating interaction and scale values + +Wrong: + +```ts +createMark(initialize) +``` + +Correct: + +```ts +createMarkWithScaleValues(initialize) +``` + +Use the exceptional factory when an interval or layout focuses one semantic value but materializes different endpoint types on its scales. + +Source: `API-FRICTION.md` F-094; `docs/reference/types.md` + +### HIGH Running side effects in resolved layout + +Wrong: update application state, mutate cached rows, allocate a persistent controller, or read external changing state from `resolveLayout`. + +Correct: derive the returned layout solely from inputs and capture local derived rows in its render closure. + +Margin and responsive solving may evaluate the callback repeatedly. + +Source: hexbin and Sankey references + +### HIGH Tension: rich interaction versus portable rendering + +Custom DOM behavior is easy to prototype but breaks renderer parity. Prefer renderer-neutral points, focus guides, controls, and semantic application state; isolate platform code in the host seam. + +See also: `build-chart-interactions/SKILL.md` and `ship-accessible-charts/SKILL.md` + +## References + +- [Extension protocol matrix](references/extension-protocols.md) + +See also: `compose-marks-and-views/SKILL.md` and `debug-and-verify-charts/SKILL.md` — justify extensions against native composition and verify them across renderer boundaries. diff --git a/packages/charts-core/skills/extend-tanstack-charts/references/extension-protocols.md b/packages/charts-core/skills/extend-tanstack-charts/references/extension-protocols.md new file mode 100644 index 00000000..2f596895 --- /dev/null +++ b/packages/charts-core/skills/extend-tanstack-charts/references/extension-protocols.md @@ -0,0 +1,43 @@ +# Extension Protocol Matrix + +| Need | Boundary | Must declare/own | Verify | +| --------------------------------------------- | ------------------------------------- | -------------------------------------------------------------------- | -------------------------------------------------------- | +| Reusable bundle of ordinary marks | `compositeMark` | parent ID; unique child IDs; child paint/point order | namespaced keys, motion, no nested resolved layouts | +| New geometry in existing scene model | `createMark` | channels, stable ID, keyed nodes, optional points | domains, finite geometry, every renderer | +| Point value differs from scale values | `createMarkWithScaleValues` | separate point and scale generic contracts | guides/domains versus focus payload | +| Geometry revealed by focus but not targetable | `focusAnchors` | semantic match values and keyed target node | no extra keyboard/pointer point | +| Cursor-driven data-less presentation | focus-guide mark | projection and resolve callback | under/over placement and viewport translation | +| Packing/collision/topology uses final pixels | `resolveLayout` | initial positional channels; pure resolved layout | repeat evaluation, resize topology, margins | +| Labels affect automatic margins | `layoutLabels` | deterministic positioned label nodes | repeated measurement and final render agreement | +| Custom callable mapping | configured scale | domain behavior, copy, mapping, optional invert/ticks | responsive range assignment and guide compatibility | +| Context-aware mapping/legend | `ChartScale` / color/legend extension | responsive context and scene output | domain/range semantics, layout reservation | +| Alternative nearest-point lookup | spatial index factory | return original points within requested distance | scene replacement and dense workloads | +| Different focus strategy | focus strategy | resolve/group/keyboard order | pointer-keyboard equivalence | +| Reusable semantic gesture | chart control | controlled signal, fallback nodes, host control lifecycle | unique IDs, capture/cancel/teardown/static fallback | +| Different mounted surface | `ChartRenderer` / `ChartSurface` | prerender, adopt/mount, paint, coordinate conversion, focus, cleanup | SSR shell, update, presentation points, destroy | +| Different SVG serialization | SVG renderer | complete accessible scene/resources | escaped text, gradients, clipping, IDs | +| Application-specific rich UI | host/application overlay | semantic state and lifecycle | positioning from scene/point, focus containment, cleanup | + +## Custom Mark Invariants + +- Initial channels declare every positional domain value. +- Resolved positional channels cannot retroactively re-domain x/y. +- Scene nodes and interaction points have deterministic keys. +- Geometry is finite and local to translated groups. +- Interaction points preserve original datum and semantic x/y values. +- Decorative geometry emits no points. +- `resolveLayout` and `layoutLabels` are synchronous, pure, and repeatable. +- Rendering uses supplied resolved scales and bounds, not parallel range math. +- No private imports, DOM access, suppressed type errors, or renderer assumptions. + +## Renderer Invariants + +- Deterministic server shell or markup. +- Compatible client adoption. +- Scene and focus paint are separable. +- `clientToScene` exists when pointer controls require coordinate conversion. +- Presentation-point subscriptions exist when animation moves hit targets. +- Renderer replacement and destroy release every resource. +- Host retains sizing, runtime, keyboard, tooltip, selection, and focus-strategy ownership. + +Source: `docs/guides/custom-marks-and-renderers.md`; `docs/reference/custom-extensions.md` diff --git a/packages/charts-core/skills/migrate-to-tanstack-charts/SKILL.md b/packages/charts-core/skills/migrate-to-tanstack-charts/SKILL.md new file mode 100644 index 00000000..a7df871c --- /dev/null +++ b/packages/charts-core/skills/migrate-to-tanstack-charts/SKILL.md @@ -0,0 +1,157 @@ +--- +name: migrate-to-tanstack-charts +description: > + Migrate Observable Plot, Recharts, Chart.js, ECharts, direct D3, or another + renderer to TanStack Charts by translating semantic ownership and proving + visual, interaction, accessibility, export, bundle, and performance parity. +metadata: + type: composition + library: '@tanstack/charts' + library_version: '0.9.0' +requires: + - design-a-chart + - debug-and-verify-charts +sources: + - 'TanStack/charts:docs/guides/migrating.md' + - 'TanStack/charts:packages/charts-core-d3/docs/observable-plot-migration.md' + - 'TanStack/charts:packages/charts-core-d3/docs/tanstack-stats-migration.md' +--- + +This skill requires `design-a-chart` and `debug-and-verify-charts`. Read them first to preserve the analytical task and define parity evidence. + +# Migrate Charts to TanStack Charts + +Use **trigger → inspect → decide → build → verify**. Translate semantic ownership, not component names or generated DOM. Keep the old chart available until parity evidence passes. + +## Integration Setup + +Inventory the current chart before changing code: + +```ts +export interface MigrationInventory { + question: string + rowGrain: string + metrics: readonly string[] + transforms: readonly string[] + layers: readonly string[] + domains: readonly string[] + interactions: readonly string[] + accessibility: readonly string[] + exports: readonly string[] + performanceBudget: string +} + +export const inventory: MigrationInventory = { + question: 'How did weekly revenue change by product?', + rowGrain: 'one row per product and week', + metrics: ['revenue USD', 'orders'], + transforms: ['weekly aggregation'], + layers: ['line per product', 'target rule'], + domains: ['shared weekly x', 'zero-based revenue y', 'stable product color'], + interactions: ['grouped x tooltip', 'keyboard inspection'], + accessibility: ['figure label', 'adjacent summary'], + exports: ['static SVG'], + performanceBudget: '12 charts, 2,000 visible points, responsive updates', +} +``` + +Record which layer currently owns every item. Do not assume the source renderer owns transforms, domains, or product state just because its API config mentions them. + +## Core Integration Patterns + +### Translate through the grammar + +For each source layer, map: + +1. source rows and row grain; +2. eager analytical transforms; +3. mark-local layout; +4. positional, grouping, color, and interval channels; +5. semantic scale domains; +6. marks and layer order; +7. focus and controlled application state; +8. renderer, adapter, and export lifecycle. + +Use the source-specific references only after this inventory. + +### Migrate one ownership boundary at a time + +A safe sequence is: + +1. Freeze source data preparation and capture fixtures. +2. Build a static TanStack scene from the same prepared rows. +3. Match scales, marks, labels, and empty/missing behavior. +4. Add focus and tooltip semantics. +5. Add controlled selection, brush, zoom, or application overlays. +6. Wire the target framework adapter and SSR lifecycle. +7. Compare bundle and sustained interaction/update performance. +8. Remove the source renderer only after rollback is no longer needed. + +### Prove parity by scenarios + +| Surface | Evidence | +| ------------- | ---------------------------------------------------------------- | +| Data | fixture rows and transform outputs match | +| Visual | geometry, domains, guides, labels, color, missing/negative cases | +| Interaction | pointer and keyboard focus, grouping, pinning, controlled state | +| Lifecycle | mount, update, resize, SSR/hydration, destroy | +| Accessibility | meaningful name, equivalent values/actions, summary/table | +| Export | SVG/Canvas/static output and fonts/resources | +| Packaging | packed consumer imports and retained optional modules | +| Performance | target dashboard shape under sustained pointer/update work | + +### Keep a comparison switch + +During production migration, use the same prepared rows and accepted application state for old and new renderers. A temporary feature flag or development harness should switch rendering ownership without changing analytics. + +## Common Mistakes + +### CRITICAL Translating component names one for one + +Wrong: find the TanStack component with the closest source-library name. + +Correct: map data, transform, scale, geometry, interaction, and lifecycle ownership independently. + +Source components combine responsibilities differently, so name matching preserves syntax instead of behavior. + +Source: `docs/guides/migrating.md` + +### CRITICAL Replacing transforms and renderer together + +Wrong: rewrite grouping, stacking, forecasting, and rendering in one change. + +Correct: freeze and test analytical rows first; migrate renderer ownership against those fixtures; move transforms later only with separate evidence. + +Changing both makes visual drift impossible to classify. + +Source: `docs/guides/migrating.md`; archived TanStack Stats migration notes + +### CRITICAL Calling screenshot similarity complete parity + +Wrong: approve after one viewport screenshot resembles the source. + +Correct: test the scenario matrix for focus, keyboard, updates, pinning, export, packages, and performance. + +A final image does not prove behavior or lifecycle ownership. + +Source: `API-FRICTION.md` F-036, F-073, F-081; `docs/guides/migrating.md` + +### HIGH Reimplementing source internals blindly + +Wrong: port D3 selections, plugin lifecycle, source scale math, and every private helper into custom marks. + +Correct: keep proven application/D3/SQL preparation at first, express supported semantics through TanStack primitives, and extend only a verified gap. + +Blind ports preserve accidental architecture and bypass renderer-neutral ownership. + +Source: `docs/guides/migrating.md`; `API-FRICTION.md` F-127 + +## References + +- [Observable Plot](references/observable-plot.md) +- [Recharts](references/recharts.md) +- [Chart.js](references/chart-js.md) +- [ECharts](references/echarts.md) +- [Direct D3](references/d3.md) + +See also: `design-a-chart/SKILL.md` and `debug-and-verify-charts/SKILL.md` — restate the analytical task and prove parity beyond screenshots. diff --git a/packages/charts-core/skills/migrate-to-tanstack-charts/references/chart-js.md b/packages/charts-core/skills/migrate-to-tanstack-charts/references/chart-js.md new file mode 100644 index 00000000..fe629771 --- /dev/null +++ b/packages/charts-core/skills/migrate-to-tanstack-charts/references/chart-js.md @@ -0,0 +1,41 @@ +# Migrate From Chart.js + +Chart.js combines dataset configuration, controllers, plugins, a Canvas renderer, and imperative instance updates. Separate those responsibilities before translating them. + +## Ownership Map + +| Chart.js concern | Inspect | TanStack destination | +| -------------------------- | ------------------------------------------ | ---------------------------------------------------------- | +| `data.labels` and datasets | row identity, implicit x, dataset grouping | explicit rows and channels per mark | +| dataset `type` | geometry and mixed-chart layering | mark family and declaration order | +| `parsing` | field mapping and coercion | typed row preparation and channel accessors | +| scale config | domain, stack, reverse, tick callback | factory/instance, mark layout, axis guide | +| plugin | lifecycle, drawing, events, external UI | mark, control, renderer/host extension, or application UI | +| imperative `update` | changed data/options and animation mode | new definition identity passed to host/adapter | +| Canvas output | raster performance and export | choose TanStack Canvas or SVG/static renderer deliberately | + +## Sequence + +1. Convert labels plus parallel dataset arrays into typed semantic rows. +2. Preserve dataset IDs as series and datum IDs as keys. +3. Classify each plugin: geometry, interaction, application UI, export, or lifecycle. +4. Recreate scale domains and mark layouts independently of Canvas pixels. +5. Rebuild the definition when accepted data/options change; do not mutate a retained chart spec. +6. Choose SVG or Canvas from actual mark count and interaction/export requirements. + +## High-Risk Differences + +- Parallel arrays hide row identity. Create rows before comparing updates or tooltips. +- Chart.js plugins can draw arbitrary Canvas pixels. A TanStack replacement must declare semantic scale values and renderer-neutral scene ownership unless it is deliberately renderer-specific. +- Dataset order may control stacking, legend, tooltip, and paint at once. Split those policies explicitly. +- Canvas screenshot parity does not prove keyboard accessibility or semantic focus points. + +## Parity Cases + +- Mixed chart layers paint in the same semantic order. +- Dataset filtering retains color and domain policy. +- Plugin annotations become stable marks rather than after-draw mutations. +- Imperative updates preserve keys and latest-wins behavior. +- Raster export, pixel ratio, and sustained pointer work meet the target budget. + +Source: `docs/guides/migrating.md`; `docs/guides/custom-marks-and-renderers.md` diff --git a/packages/charts-core/skills/migrate-to-tanstack-charts/references/d3.md b/packages/charts-core/skills/migrate-to-tanstack-charts/references/d3.md new file mode 100644 index 00000000..62eb61e0 --- /dev/null +++ b/packages/charts-core/skills/migrate-to-tanstack-charts/references/d3.md @@ -0,0 +1,50 @@ +# Migrate From Direct D3 + +Direct D3 charts often mix data preparation, scale construction, layout, DOM joins, transition state, gestures, and application UI. Keep proven algorithms; migrate ownership boundary by boundary. + +## Classify Existing Code + +| D3 code | Usually keep first | TanStack destination | +| --------------------------------- | ------------------------------------------- | -------------------------------------------------------- | +| `d3-array` aggregation/statistics | yes, if tested and semantic | prepared rows or later TanStack transform | +| `d3-time` interval policy | yes | prepared rows, domains, tick values | +| scale domain construction | preserve semantic policy | configured scale instance/factory | +| scale pixel range | no | Charts assigns final responsive range | +| shape/layout algorithm | keep if unsupported or application-specific | feed rows to marks or resolved-layout extension | +| selection/data join | no | scene reconciliation through mark/datum identity | +| axis DOM rendering | no | chart guides | +| transition mutation | no | SVG animation or motion renderer | +| pointer/brush/zoom DOM listeners | classify product policy | first-party controlled control or application controller | +| HTML overlay | maybe | application UI anchored from scene/point semantics | + +## Sequence + +1. Freeze prepared rows, domains, and layout outputs as fixtures. +2. Replace DOM joins with built-in marks using the same rows and stable keys. +3. Move axis and legend ownership to the definition. +4. Remove authored positional ranges; let final layout assign them. +5. Replace transition code after static/update geometry matches. +6. Replace gestures with controlled semantic state after focus behavior matches. +7. Move a D3 algorithm only when the native transform/layout proves equivalent. + +## Extension Boundary + +Keep an existing D3 algorithm when it produces meaningful data-space rows or a final-bounds topology that TanStack does not own. Wrap it in the narrowest correct layer: + +- application transform for reusable semantic rows; +- built-in marks for the output geometry; +- resolved-layout custom mark for final-pixel topology; +- renderer/host extension only for genuinely platform-specific behavior. + +Do not call D3 selection APIs from scene generation. + +## Parity Cases + +- Data join keys through insert, delete, and reorder. +- Domain and tick behavior at empty, negative, log-invalid, and missing cases. +- Resize with measured margins and any final-bounds layout. +- Interrupted transitions and active focus. +- Gesture cancellation, keyboard alternative, and teardown. +- Packed bundle no longer retains obsolete D3 DOM/gesture modules. + +Source: `docs/guides/migrating.md`; `docs/concepts/scales-and-d3.md`; `docs/guides/custom-marks-and-renderers.md` diff --git a/packages/charts-core/skills/migrate-to-tanstack-charts/references/echarts.md b/packages/charts-core/skills/migrate-to-tanstack-charts/references/echarts.md new file mode 100644 index 00000000..072ad43d --- /dev/null +++ b/packages/charts-core/skills/migrate-to-tanstack-charts/references/echarts.md @@ -0,0 +1,43 @@ +# Migrate From ECharts + +ECharts centralizes datasets, series, coordinates, components, actions, and renderer state in an option object. Translate each semantic owner separately; do not wrap the option object in a custom renderer. + +## Ownership Map + +| ECharts concern | Inspect | TanStack destination | +| ------------------------- | ----------------------------------------------- | -------------------------------------------- | +| `dataset` and `transform` | row grain, dimensions, lineage | application/TanStack eager transforms | +| `series` | geometry, encode map, stack, data overrides | mark-local data and channels | +| axis/grid | domains, coordinate system, labels, containment | scales, guides, measured margins | +| `visualMap` | ordered/categorical color policy | chart color scale and legend | +| `axisPointer` and tooltip | snap/group/content/linking | focus, crosshair, tooltip, cursor controller | +| `dataZoom` | accepted semantic window and gesture policy | controlled zoom/brush plus axis viewport | +| `dispatchAction` | application command/state mutation | controlled application state/controller | +| graphic/custom series | annotation, UI, or custom geometry | marks, application overlay, or custom mark | + +## Sequence + +1. Materialize the dataset rows and every transform output as fixtures. +2. Expand `encode` and inherited dimensions into explicit mark channels. +3. Separate series stack/group identity from color. +4. Translate `visualMap` domains and thresholds as application-owned color policy. +5. Replace linked axis pointers with semantic cursor controllers, not pixel broadcasts. +6. Replace `dataZoom` with controlled domains and first-party controls or application UI. +7. Verify renderer, export, and action-driven workflows independently. + +## High-Risk Differences + +- `containLabel`-style containment is not label collision policy. Configure candidate density and thinning. +- ECharts actions can mutate internal renderer state. TanStack definitions and controlled signals require the application to own accepted semantic state. +- `custom` series render callbacks often operate in coordinate-system pixels. Reframe them as declared scale channels plus renderer-neutral scene nodes. +- Connected charts must share values/domains, not copy axis-pointer pixels. + +## Parity Cases + +- Dataset transform outputs and dimension names. +- Stack order, hidden-series domains, and stable color mappings. +- Axis-pointer snap/group behavior by pointer and keyboard. +- Zoom clamping, cancellation, reset, and programmatic changes. +- Graphic annotations after resize, SSR, and export. + +Source: `docs/guides/migrating.md`; interaction and scale guides diff --git a/packages/charts-core/skills/migrate-to-tanstack-charts/references/observable-plot.md b/packages/charts-core/skills/migrate-to-tanstack-charts/references/observable-plot.md new file mode 100644 index 00000000..ca924df2 --- /dev/null +++ b/packages/charts-core/skills/migrate-to-tanstack-charts/references/observable-plot.md @@ -0,0 +1,43 @@ +# Migrate From Observable Plot + +Observable Plot and TanStack Charts both use marks, channels, transforms, scales, and facets, but their ownership boundaries differ. Translate the meaning of each Plot mark rather than copying its generated SVG or expecting every option name to match. + +## Inventory + +| Plot concern | Inspect | TanStack destination | +| ------------------- | ------------------------------------------- | ----------------------------------------------------------- | +| `Plot.plot` options | dimensions, margins, style, scales, legends | chart definition plus host sizing/theme | +| mark constructor | source rows, channels, mark-local transform | first-party mark or composition | +| Plot transform | output row grain and ordering | eager TanStack/application transform or mark layout | +| scale option | inferred/fixed domain, clamp, nice, reverse | factory versus configured instance; no authored pixel range | +| facet | shared/independent axes and domains | `facetChart`, `facet`, or named views | +| pointer tip | grouping, nearest policy, content | focus strategy, tooltip, crosshair, optional portal | +| generated SVG | accessibility and application overlays | renderer output; never migration input | + +## Sequence + +1. Capture the rows passed to every Plot mark after application preparation. +2. Record mark order and which layer owns interaction. +3. Separate Plot transforms into reusable data work versus geometry-only layout. +4. Recreate factories or configured scale domains; omit positional ranges. +5. Build the smallest equivalent mark composition. +6. Add focus grouping and tooltip content from semantic points. +7. Compare facets, empty values, inferred domains, and responsive guide margins. + +## High-Risk Differences + +- Plot convenience marks may combine multiple TanStack layers; preserve semantics, not constructor count. +- Plot's generated SVG classes and nodes are not a stable contract. Do not query or move them. +- A Plot transform may change row grain or retain source records differently. Assert output rows and tooltip lineage. +- Plot dimensions often live in the plot options. In TanStack Charts, container width and host height are separate from definition semantics. +- Plot pointer interactions may infer a presentation that needs explicit `focus`, tooltip, crosshair, or controlled state. + +## Parity Cases + +- Same fixture at narrow and wide containers. +- Missing positional values and explicit intervals. +- Facet domain policy and outer versus cell axes. +- Grouped tooltip order and keyboard focus. +- Exported static SVG without source-library DOM dependencies. + +Source: `docs/guides/migrating.md`; `packages/charts-core-d3/docs/observable-plot-migration.md` diff --git a/packages/charts-core/skills/migrate-to-tanstack-charts/references/recharts.md b/packages/charts-core/skills/migrate-to-tanstack-charts/references/recharts.md new file mode 100644 index 00000000..0a61ff83 --- /dev/null +++ b/packages/charts-core/skills/migrate-to-tanstack-charts/references/recharts.md @@ -0,0 +1,43 @@ +# Migrate From Recharts + +Recharts expresses a chart through a React component tree. TanStack Charts expresses one reusable definition and lets the React adapter own mount/update lifecycle. Do not translate JSX node names one for one. + +## Ownership Map + +| Recharts concern | Inspect | TanStack destination | +| ----------------------------- | ---------------------------------------------------- | --------------------------------------------------- | +| chart container component | shared data and layout direction | prepared rows plus definition | +| `` | parent sizing, aspect, debounce | adapter container measurement; height/aspect policy | +| `` / `` | data key, domain, type, ticks, formatter | positional scale and axis guide | +| `` / `` / `` | data key, stack ID, shape, animation | mark channels, `z`, stack/group layout, stable key | +| `` | active payload, label, formatter, portal assumptions | definition tooltip and optional adapter body | +| `` | series identity and visibility | color scale/legend or controlled interactive legend | +| `` | accepted domain and application policy | controlled brush/zoom or application control | +| custom shape | semantic geometry versus DOM convenience | built-in composition, then custom mark if required | + +## Sequence + +1. Normalize implicit chart-level data and per-series data into explicit mark-local rows. +2. Record `dataKey`, `stackId`, axis IDs, and hidden-series domain behavior. +3. Replace implicit component inheritance with explicit channels and scales. +4. Keep definition construction memoized against captured React values. +5. Move only rich tooltip body composition to the React adapter; keep tooltip behavior in the definition. +6. Test mount/unmount, Strict Mode development lifecycle, SSR, and container resize. + +## High-Risk Differences + +- Recharts component nesting looks like semantic ownership but often supplies inherited data/config. Make every TanStack mark's data and channels explicit. +- `stackId` is not merely color grouping. Choose native stack layout and explicit `z`/color semantics. +- Recharts custom shapes often receive already-resolved pixels. TanStack custom marks must remain renderer-neutral and declare scale values before render. +- Do not recreate the definition on every React render. +- Do not move tooltip focus behavior onto adapter props; only the body renderer is framework-owned. + +## Parity Cases + +- Filtered series retain intended domains and colors. +- Stacks handle missing and negative values. +- Tooltip group order, pinning, and keyboard traversal match the product task. +- Responsive layout works without a fixed-width wrapper. +- Server output and hydration use deterministic initial geometry. + +Source: `docs/guides/migrating.md`; React adapter documentation diff --git a/packages/charts-core/skills/prepare-chart-data/SKILL.md b/packages/charts-core/skills/prepare-chart-data/SKILL.md new file mode 100644 index 00000000..c7f45706 --- /dev/null +++ b/packages/charts-core/skills/prepare-chart-data/SKILL.md @@ -0,0 +1,214 @@ +--- +name: prepare-chart-data +description: > + Order, aggregate, derive, and transform application rows for TanStack + Charts while preserving semantic units and lineage. Load for stacks, bins, + groups, rolling windows, ranks, regression, hierarchy, missing values, or + reactive transform work. +metadata: + type: core + library: '@tanstack/charts' + library_version: '0.9.0' +sources: + - 'TanStack/charts:docs/guides/transforms-and-reactivity.md' + - 'TanStack/charts:docs/reference/transforms.md' + - 'TanStack/charts:docs/reference/marks/line-and-area.md' +--- + +# Prepare Chart Data + +Use **trigger → inspect → decide → build → verify**. Keep analytical preparation outside responsive layout and preserve the row lineage needed by tooltips, selection, tables, and drill-down. + +## Setup + +Prepare semantic rows once, then pass them to marks: + +```ts +import { groupBy, lineY, rollingWindow } from '@tanstack/charts' + +interface Order { + day: string + region: string + amount: number +} + +export function prepareRevenue(orders: readonly Order[]) { + const daily = groupBy(orders, { + by: { region: 'region', day: 'day' }, + outputs: { + revenue: { value: 'amount', reduce: 'sum' }, + orders: { reduce: 'count' }, + }, + }) + + return rollingWindow(daily, { + by: 'region', + orderBy: 'day', + size: 7, + partial: false, + outputs: { revenue7d: { value: 'revenue', reduce: 'sum' } }, + }) +} + +const prepared = prepareRevenue([ + { day: '2026-08-01', region: 'West', amount: 120 }, + { day: '2026-08-02', region: 'West', amount: 160 }, +]) + +export const revenueMark = lineY(prepared, { + x: 'day', + y: 'revenue7d', + z: 'region', +}) +``` + +## Core Patterns + +### Choose the owner by scope + +| Work | Owner | +| ---------------------------------------- | ------------------------------------------ | +| Calculation from one row | channel accessor | +| Reusable cross-row result | eager data transform | +| Geometry meaningful only inside one mark | `layout: stack()` or `layout: group()` | +| Product-specific enrichment or filtering | ordinary application function | +| Pixel-space work after margins resolve | resolved-layout mark, not a data transform | + +### Compose structural and analytical transforms + +```ts +import { normalize, select } from '@tanstack/charts' +import { fold } from '@tanstack/charts/transform/fold' + +const services = [ + { service: 'api', latency: 180, throughput: 820 }, + { service: 'worker', latency: 240, throughput: 510 }, +] + +const folded = fold(services, { + fields: ['latency', 'throughput'] as const, + as: { key: 'metric', value: 'measurement' }, +}) + +const normalized = normalize(folded, { + by: 'metric', + value: 'measurement', + basis: 'extent', + as: 'relativeMeasurement', +}) + +export const baselines = select(normalized, { + by: 'metric', + select: 'first', +}) +``` + +Each stage should have one semantic responsibility and a named output. + +### Order path data before mark construction + +```ts +import { lineY } from '@tanstack/charts' + +const rows = [ + { date: new Date('2026-08-03'), value: 14 }, + { date: new Date('2026-08-01'), value: 10 }, + { date: new Date('2026-08-02'), value: 12 }, +] + +const ordered = [...rows].sort((left, right) => +left.date - +right.date) + +export const trend = lineY(ordered, { x: 'date', y: 'value' }) +``` + +Scale domains do not reorder line or area topology. + +### Preserve direct lineage + +TanStack transforms record their immediate input in `source`. If application code creates additional derived rows, preserve equivalent references when focus, drill-down, or audit must reach the original observations. + +Read [the transform decision table](references/transforms.md) before combining transform families. + +## Common Mistakes + +### CRITICAL Sorting after creating the mark + +Wrong: + +```ts +const mark = lineY(rows, { x: 'date', y: 'value' }) +rows.sort((left, right) => +left.date - +right.date) +``` + +Correct: + +```ts +const ordered = [...rows].sort((left, right) => +left.date - +right.date) +const mark = lineY(ordered, { x: 'date', y: 'value' }) +``` + +Line and area marks capture input order as path order. + +Source: `docs/reference/marks/line-and-area.md` + +### HIGH Running eager transforms in responsive builders + +Wrong: + +```ts +defineChart(({ width }) => ({ + marks: [lineY(rollingWindow(rows, options), channels)], +})) +``` + +Correct: + +```ts +const prepared = rollingWindow(rows, options) +defineChart(({ width }) => ({ marks: [lineY(prepared, channels)] })) +``` + +Responsive builders may rerun for size and layout changes while source rows remain unchanged. + +Source: `docs/guides/transforms-and-reactivity.md`; `API-FRICTION.md` F-128 + +### HIGH Manually accumulating ordinary stacks + +Wrong: maintain application running totals for each x value and series. + +Correct: + +```ts +barY(rows, { + x: 'quarter', + y: 'revenue', + z: 'product', + layout: stack(), +}) +``` + +Native stack layout owns missing series, negative values, order, and updates. Fixed in the current API, but agents trained on early examples may still generate manual accumulation. + +Source: GitHub issue 9; `API-FRICTION.md` F-163 + +### HIGH Flattening derived rows without lineage + +Wrong: replace aggregated rows with unlabeled numeric tuples. + +Correct: keep named outputs, grouping fields, and direct source references through each application-owned step. + +Tooltips, selection, and drill-down otherwise lose the records that contributed to the value. + +Source: `docs/reference/transforms.md`; `docs/guides/transforms-and-reactivity.md` + +### HIGH Tension: analytical honesty versus visual simplicity + +Aggregation and normalization reduce visual noise but can hide denominators, sample sizes, uncertainty, and missing-value policy. Prepare the evidence required by `design-a-chart` before reducing row detail. + +See also: `design-a-chart/SKILL.md` § Common Mistakes + +## References + +- [Transform selection and output contracts](references/transforms.md) + +See also: `configure-scales-guides-color/SKILL.md`, `build-chart-interactions/SKILL.md`, and `coordinate-charts-with-tanstack/SKILL.md` — derived values determine domains, legends, tooltip content, available lineage, and shared chart-grid projections. diff --git a/packages/charts-core/skills/prepare-chart-data/references/transforms.md b/packages/charts-core/skills/prepare-chart-data/references/transforms.md new file mode 100644 index 00000000..f5b66ca7 --- /dev/null +++ b/packages/charts-core/skills/prepare-chart-data/references/transforms.md @@ -0,0 +1,43 @@ +# Transform Selection and Output Contracts + +Use this table to choose analytical ownership. Read `docs/reference/transforms.md` for exact signatures. + +| Need | Primitive | Input and output contract | Preserve or verify | +| ------------------------------ | -------------------------------- | ----------------------------------------- | --------------------------------------------- | +| Wide fields become metric rows | `fold` | rows → one row per selected field | field tuple, metric direction, lineage | +| Aggregate by named keys | `groupBy` | rows → one row per group | group fields, empty groups, reducer semantics | +| Numeric histogram | `binX` / `binY` | observations → interval rows | thresholds, inclusive edges, sample count | +| Two-dimensional bins | `binXY` | observations → x/y interval cells | empty cells, count or aggregate output | +| Calendar bins | time-bin transform | dates → calendar interval rows | timezone, interval boundary, missing periods | +| Reusable stack endpoints | `stackRowsX` / `stackRowsY` | series rows → endpoint rows | negative policy, order, missing series | +| Mark-local stacking | `stack()` layout | ordinary rows → mark geometry | use when endpoints have no external consumer | +| Mark-local grouping | `group()` layout | ordinary rows → grouped mark geometry | bandwidth and series order | +| Normalize values | `normalize` | rows → rows with named normalized output | basis, denominator, zero extent | +| Rank categories | `rank` | rows → rank output | direction, ties, stable secondary order | +| Rolling statistic | `rollingWindow` | ordered rows → windowed rows | partition, order, size, partial-window policy | +| Running total | `cumulative` | ordered rows → cumulative output | reset partition and missing values | +| Select representative rows | `select` | rows → original chosen rows | order and tie policy | +| Reduce with custom output | `reduce` / reducer callback | group/window → scalar or row | deterministic, synchronous reducer | +| Regression | regression transform/mark | observations → fitted values or geometry | model assumptions, residual evidence | +| Waterfall | waterfall transform | changes → start/end contribution rows | reconciliation to starting and ending totals | +| Mosaic | mosaic transform | categorical observations → area intervals | marginal totals and empty combinations | +| Hierarchy | hierarchy preparation | parent-child or nested rows → hierarchy | root policy, cycles, negative values | +| Product-specific enrichment | ordinary `map`/`filter`/function | application rows → application rows | named units and explicit lineage | + +## Sequence Rules + +1. Fix row grain and units before aggregation. +2. Apply structural transforms before analytics that depend on the new structure. +3. Partition and order before rolling or cumulative transforms. +4. Keep geometry-only stack/group work inside the mark. +5. Hoist eager transforms out of chart builders and render functions. +6. Recompute when source rows or semantic options change; do not mutate results in place. +7. Verify empty, single-row, missing, duplicate, negative, and zero-denominator cases. + +## Lineage Rule + +Every derived row used for inspection should answer: “Which immediate rows produced this value?” TanStack transforms use `source`; application transforms should retain an equivalent field when the answer matters. + +## Projection Rule + +Forecasting is not a display transform. Keep model execution outside the chart, then deliver rows that explicitly identify observed versus projected status, cutoff, horizon, and interval endpoints. diff --git a/packages/charts-core/skills/ship-accessible-charts/SKILL.md b/packages/charts-core/skills/ship-accessible-charts/SKILL.md new file mode 100644 index 00000000..ba24549f --- /dev/null +++ b/packages/charts-core/skills/ship-accessible-charts/SKILL.md @@ -0,0 +1,225 @@ +--- +name: ship-accessible-charts +description: > + Ship TanStack Charts with meaningful accessibility, deterministic SSR and + hydration, correct adapter lifecycle, renderer and export choices, exact + package subpaths, and explicit React Native validation. +metadata: + type: lifecycle + library: '@tanstack/charts' + library_version: '0.9.0' +sources: + - 'TanStack/charts:docs/guides/accessibility.md' + - 'TanStack/charts:docs/guides/ssr-and-hydration.md' + - 'TanStack/charts:docs/guides/exporting.md' + - 'TanStack/charts:docs/framework/*/adapter.md' + - 'TanStack/charts:packages/react-native-charts/README.md' +--- + +# Ship Accessible Charts + +Run **trigger → inspect → decide → build → verify** before release. A chart is shipped only when its semantic alternative, adapter lifecycle, SSR policy, renderer, exports, package boundaries, and teardown are proven in a consumer-shaped scenario. + +## Accessibility Checks + +### Check: the name identifies the comparison + +Expected: + +```ts +const hostOptions = { + definition, + height: 320, + initialWidth: 640, + ariaLabel: 'Weekly downloads for Core and React packages', + ariaDescription: 'Values are seven-day totals. Missing weeks appear as gaps.', +} +``` + +Fail condition: the label says only “chart”, repeats a visible heading without the metric, or omits the comparison and period. + +Fix: name the metric, compared entities, and time scope; put conclusions and detailed values in normal application content. + +### Check: critical values have an equivalent representation + +Expected: a visible heading and units, plus an adjacent summary or semantic table when precise values or application decisions depend on the chart. + +Fail condition: the only way to retrieve an essential value is pointer hover, color, motion, or visual estimation. + +Fix: bind the same semantic rows or selected key to application text/table controls. + +### Check: every interaction has a non-pointer path + +Expected: native point keyboard navigation, semantic buttons/inputs for free cursors and range controls, visible focus, cancel/reset paths, and meaningful committed-state text. + +Fail condition: a transparent pointer overlay is the sole control surface. + +Fix: use first-party controls where their keyboard contract fits and application-owned semantic controls otherwise. + +## Lifecycle Checks + +### Check: SSR uses a supported adapter and deterministic inputs + +Expected: + +```ts +import { + createChartRuntime, + defineChart, + lineY, + renderChartSvg, +} from '@tanstack/charts' +import { scaleLinear } from '@tanstack/charts/scales/linear' +import { scaleUtc } from 'd3-scale' + +interface TrafficRow { + date: Date + visits: number +} + +const rows: readonly TrafficRow[] = [ + { date: new Date('2026-08-10T00:00:00Z'), visits: 820 }, + { date: new Date('2026-08-11T00:00:00Z'), visits: 910 }, +] + +const definition = defineChart({ + marks: [lineY(rows, { x: 'date', y: 'visits' })], + x: { scale: scaleUtc }, + y: { scale: scaleLinear }, +}) + +const runtime = createChartRuntime() +const scene = runtime.render(definition, { width: 720, height: 400 }) + +export const svg = renderChartSvg(scene, { + ariaLabel: 'Daily traffic', + idPrefix: 'daily-traffic', +}) + +runtime.destroy() +``` + +Fail condition: definition construction reads browser layout, random values, local time, or unresolved async data; or the selected adapter does not promise SSR. + +Fix: resolve data before render, use deterministic formatting and dimensions, and follow the selected adapter reference. + +### Check: mount, update, and cleanup stay adapter-owned + +Expected: one runtime per mounted adapter instance, complete immutable option updates, stable definitions until captured values change, and cleanup on unmount/disconnect. + +Fail condition: application code calls DOM measurement or mounts a browser host during server render, or retains observers/listeners after removal. + +Fix: use the adapter lifecycle or `mountChart` only in a browser-owned mount phase and call `destroy()` at teardown. + +## Renderer and Export Checks + +### Check: renderer choice matches the task + +- SVG: visible server geometry, vector export, DOM styling, ordinary interactive charts. +- Canvas: high scene counts or raster-first output; server emits an accessible shell, not pixels. +- Static SVG: deterministic server/export artifact without browser interaction. +- React Native SVG: explicit native target with device-level validation. + +### Check: export is reproducible + +Expected: explicit dimensions, theme/background, scoped IDs, portable fonts/assets, intentional focus inclusion, and a meaningful exported name/description. + +Fail condition: export depends on current responsive pixels or unreachable application CSS/resources. + +Fix: render an explicit scene or serialize/rasterize with explicit artifact policy. + +## Common Mistakes + +### CRITICAL Using a generic accessible label + +Wrong: `ariaLabel: 'Chart'`. + +Correct: identify the metric, entities, period, and unit needed to understand the figure. + +A generic label exposes a focusable graphic without useful identity. + +Source: `docs/guides/accessibility.md` + +### HIGH Making the chart the only representation + +Wrong: require hover or visual estimation for exact operational values. + +Correct: render a summary, table, or application controls from the same semantic data/state. + +The chart surface is supplemental when exact values are critical. + +Source: `docs/guides/accessibility.md`; archived responsive/accessibility notes + +### CRITICAL Mounting a browser host during server rendering + +Wrong: call `mountChart` or adapter DOM mount from a server lifecycle. + +Correct: prerender a deterministic scene through a supported SSR adapter or `renderChartSvg`, then mount the browser host only after a real element exists. + +Measurement and mutation require browser elements. Older Angular workarounds were especially prone to this boundary error. + +Source: GitHub issue 56; `docs/guides/ssr-and-hydration.md` + +### HIGH Putting behavior on adapter props + +Wrong: + +```tsx + +``` + +Correct: + +```tsx +const interactive = defineChart(chart, { tooltip, keyboard: true }) + +``` + +Focus, tooltip, keyboard, controls, cursors, and SVG animation belong to the reusable definition; adapters own surface lifecycle and framework body composition. The current definition API replaces legacy adapter behavior props. + +Source: chart-behavior migration in `CHANGELOG.md`; React chart reference + +### HIGH Importing the universal barrel on native + +Wrong: use `@tanstack/charts/universal` for every native chart. + +Correct: use exact core mark/scale/scene and `@tanstack/charts/react-native` subpaths unless cross-platform authoring justifies the universal bundle. + +Exact imports protect Metro and native declaration environments from unrelated browser and optional capability code. + +Source: `API-FRICTION.md` F-154, F-171, F-173, F-256; React Native package README + +### HIGH Tension: rich interaction versus portable rendering + +DOM convenience can break static SVG, Canvas, native, SSR, or keyboard equivalence. Keep semantics in definitions and application state; use platform-specific body/host extensions only at the presentation seam. + +See also: `build-chart-interactions/SKILL.md` and `extend-tanstack-charts/SKILL.md` + +## Pre-Deploy Summary + +- [ ] Meaningful name and concise description. +- [ ] Visible units, time range, and source context. +- [ ] Exact-value summary/table where required. +- [ ] Pointer, keyboard, touch, and cancel/reset scenarios. +- [ ] Reduced-motion and non-color evidence. +- [ ] Supported SSR policy with deterministic initial geometry. +- [ ] Stable definition, mark, datum, and resource IDs. +- [ ] Mount, update, resize, font relayout, and destroy verified. +- [ ] Renderer and export behavior verified from a packed consumer. +- [ ] Native claims verified on target devices when applicable. + +## References + +- [React](references/react.md) +- [Preact](references/preact.md) +- [Vue](references/vue.md) +- [Solid](references/solid.md) +- [Svelte](references/svelte.md) +- [Angular](references/angular.md) +- [Lit](references/lit.md) +- [Alpine](references/alpine.md) +- [Octane](references/octane.md) +- [React Native](references/react-native.md) +- [Renderers and export](references/renderers-and-export.md) + +See also: `debug-and-verify-charts/SKILL.md` — SSR, accessibility, renderer, and package claims need consumer-level evidence. diff --git a/packages/charts-core/skills/ship-accessible-charts/references/alpine.md b/packages/charts-core/skills/ship-accessible-charts/references/alpine.md new file mode 100644 index 00000000..998a0b86 --- /dev/null +++ b/packages/charts-core/skills/ship-accessible-charts/references/alpine.md @@ -0,0 +1,22 @@ +# Alpine Adapter + +Register `charts` from `@tanstack/charts/alpine`, then pass a complete chart options value through the directive. Alpine is a browser-only adapter contract. + +## Ownership + +- The directive creates the shared host after Alpine starts, forwards complete option changes, and destroys the runtime during directive cleanup. +- Keep focus, tooltip, selection, controls, cursor, and animation in the definition. +- Use the documented Alpine tooltip body callback only for framework-owned content. + +## Server Policy + +Render a server shell or separate static SVG through application infrastructure, then start Alpine in the browser. Do not expect the directive to hydrate chart geometry. + +## Failure Checks + +- Starting before a real browser element exists. +- Passing a partially mutated options object. +- Leaving directive cleanup disconnected from DOM removal. +- Putting buttons in transient tooltip content before pinning. + +Source: `docs/framework/alpine/adapter.md`; `docs/framework/alpine/reference/chart.md` diff --git a/packages/charts-core/skills/ship-accessible-charts/references/angular.md b/packages/charts-core/skills/ship-accessible-charts/references/angular.md new file mode 100644 index 00000000..933de970 --- /dev/null +++ b/packages/charts-core/skills/ship-accessible-charts/references/angular.md @@ -0,0 +1,22 @@ +# Angular Adapter + +Use `Chart` from `@tanstack/charts/angular` with an immutable complete options value. The verified package contract currently covers browser mount, immutable updates, and teardown; adapter SSR and hydration are not yet promised. + +## Ownership + +- The component prerenders controller state, mounts into the real surface in the browser lifecycle, forwards replacement options, and destroys on teardown. +- Keep definition behavior in the definition. +- Use `[tanstackChartTooltipBody]` with the definition as the generic type witness for Angular-owned tooltip content. + +## SSR Policy + +Do not infer adapter SSR support from an application's Angular server infrastructure. For deterministic server SVG, use the framework-neutral runtime/render boundary or keep the chart on the verified browser path until the adapter contract changes. + +## Failure Checks + +- Mounting measurement or mutation against a server placeholder. +- Mutating nested options without replacing the complete value. +- Losing tooltip types by omitting the definition witness. +- Claiming hydration parity without an application-level test. + +Source: `docs/framework/angular/adapter.md`; `docs/framework/angular/reference/chart.md`; GitHub issue 56 diff --git a/packages/charts-core/skills/ship-accessible-charts/references/lit.md b/packages/charts-core/skills/ship-accessible-charts/references/lit.md new file mode 100644 index 00000000..c6cd4b7c --- /dev/null +++ b/packages/charts-core/skills/ship-accessible-charts/references/lit.md @@ -0,0 +1,22 @@ +# Lit Adapter + +Register a custom element with `defineChartElement` from `@tanstack/charts/lit`, then replace its complete `options` value when data or policy changes. Lit SSR and hydration are not yet a verified adapter contract. + +## Ownership + +- The element prerenders through the shared controller, mounts after first update, updates from replacement options, destroys on disconnect, and mounts again on reconnect. +- Keep definition behavior inside the definition. +- Use the documented body-renderer seam only for Lit-owned tooltip content. + +## Lifecycle Checks + +- Registration happens once for a tag name. +- Disconnect releases runtime resources. +- Reconnect mounts the existing element/controller cleanly. +- Options are replaced rather than mutated in place. + +## SSR Policy + +Do not claim Lit hydration support from browser lifecycle tests. Use framework-neutral static rendering when server SVG is required, or verify the application integration explicitly. + +Source: `docs/framework/lit/adapter.md`; `docs/framework/lit/reference/chart.md` diff --git a/packages/charts-core/skills/ship-accessible-charts/references/octane.md b/packages/charts-core/skills/ship-accessible-charts/references/octane.md new file mode 100644 index 00000000..4d9cc63a --- /dev/null +++ b/packages/charts-core/skills/ship-accessible-charts/references/octane.md @@ -0,0 +1,22 @@ +# Octane Adapter + +Use `Chart` from `@tanstack/charts/octane`. Use `/canvas` for the Canvas surface and `/core` for an injected renderer. + +## Ownership + +- Keep fixed definitions at module scope and captured definitions stable through Octane `useMemo`. +- The adapter prerenders, mounts in `useLayoutEffect`, forwards complete updates, and destroys on cleanup. +- Put behavior in the definition; use `renderTooltipBody` only for Octane-owned pinned content. + +## SSR + +Octane emits complete SVG or a deterministic Canvas shell at `initialWidth`, then hydrates/adopts it in the browser target. Keep definitions, data, domains, renderer choice, dimensions, and generated IDs deterministic. + +## Failure Checks + +- Definition recreation on every component execution. +- Switching target structure between server and browser. +- Expecting Canvas pixels on the server. +- Treating tooltip body rendering as focus/placement ownership. + +Source: `docs/framework/octane/adapter.md`; `docs/framework/octane/quick-start.md`; `docs/framework/octane/reference/chart.md` diff --git a/packages/charts-core/skills/ship-accessible-charts/references/preact.md b/packages/charts-core/skills/ship-accessible-charts/references/preact.md new file mode 100644 index 00000000..acf64014 --- /dev/null +++ b/packages/charts-core/skills/ship-accessible-charts/references/preact.md @@ -0,0 +1,22 @@ +# Preact Adapter + +Use `Chart` from `@tanstack/charts/preact` and Preact's `useMemo` for definitions that capture reactive values. + +## Ownership + +- The adapter prerenders, mounts the shared host in a layout effect, forwards complete prop updates, and destroys it on unmount. +- Keep definition behavior in the definition and surface/accessibility options on `Chart`. +- Use `renderTooltipBody` for Preact-owned pinned content; the shared host owns focus, placement, portaling, and dismissal. + +## SSR + +Preact emits the complete host, surface, and accessible SVG. `initialWidth` controls server geometry, and `useId()` must see the same component tree on both sides. + +## Failure Checks + +- Definition recreation without changed captured values. +- Nondeterministic server data, formatting, dimensions, or tree order. +- Assuming a React-only tooltip import works in Preact. +- Rendering interactive controls in a transient inert tooltip body. + +Source: `docs/framework/preact/adapter.md`; `docs/framework/preact/reference/chart.md` diff --git a/packages/charts-core/skills/ship-accessible-charts/references/react-native.md b/packages/charts-core/skills/ship-accessible-charts/references/react-native.md new file mode 100644 index 00000000..c6f343f2 --- /dev/null +++ b/packages/charts-core/skills/ship-accessible-charts/references/react-native.md @@ -0,0 +1,39 @@ +# React Native Adapter + +Use the experimental SVG host from `@tanstack/charts/react-native` only when the inspected application targets React Native. Keep claims scoped to devices and configurations actually tested. + +## Exact Imports + +Prefer exact subpaths to protect Metro's bundle and declarations: + +```tsx +import { lineY } from '@tanstack/charts/line' +import { defineChart } from '@tanstack/charts/scene' +import { scaleLinear } from '@tanstack/charts/scales/linear' +import { Chart } from '@tanstack/charts/react-native' +import { tooltip } from '@tanstack/charts/react-native/tooltip' +``` + +Use `@tanstack/charts/universal` only when one definition source intentionally trades bundle floor for cross-platform portability. + +## Native Ownership + +- `react-native-svg` paints the scene; install the SDK-compatible version for Expo or the documented peer range for bare native. +- Native layout supplies dimensions or aspect ratio. +- Typography and synchronous `measureText` must reflect native font family, style, stretch, letter spacing, direction, locale, and font scale. +- Gesture responder and accessibility actions replace DOM pointer/key plumbing. +- Application state owns rich controls and equivalent semantic inputs. + +## Required Validation + +- Target iOS and Android versions, simulator and physical device where supported. +- Bare and/or Expo release build matching the application. +- Font loading and dynamic type/font scale. +- VoiceOver/TalkBack names, actions, traversal, and dismissal. +- Touch target size, tooltip pinning, gesture cancellation, and linked controls. +- Rotation, layout changes, large data, memory, and sustained interaction performance. +- Metro bundle contents from the packed package. + +Do not generalize from the current iOS simulator fixtures to broad native support. + +Source: `packages/react-native-charts/README.md`; `NATIVE-PLATFORM-SUPPORT-SPIKE.md`; `API-FRICTION.md` F-154, F-171, F-173, F-256 diff --git a/packages/charts-core/skills/ship-accessible-charts/references/react.md b/packages/charts-core/skills/ship-accessible-charts/references/react.md new file mode 100644 index 00000000..e44b458f --- /dev/null +++ b/packages/charts-core/skills/ship-accessible-charts/references/react.md @@ -0,0 +1,26 @@ +# React Adapter + +Use `Chart` from `@tanstack/charts/react`. Use `/canvas` for the Canvas host, `/core` for an injected renderer, and `/tooltip` variants only when React-owned tooltip content is required. + +## Ownership + +- Keep fixed definitions at module scope. +- Wrap definitions that capture props/state in `useMemo` with every captured semantic value. +- Put focus, tooltip, keyboard, cursor, selection, controls, and animation on the definition. +- Put `height`, `width`/`initialWidth`, `ariaLabel`, `ariaDescription`, `className`, `style`, and callbacks on `Chart`. +- Use `renderTooltipBody` only for framework-owned body content; render controls only when pinned. + +## SSR + +React emits complete SVG at `initialWidth`; Canvas emits a deterministic accessible shell. Keep data, definitions, dimensions, formatters, renderer choice, and component tree identical for server and first client render. Generated resource IDs use React identity; supply `idPrefix` for coordinated roots. + +## Failure Checks + +- Fresh definition on every render. +- Tooltip behavior on props instead of the definition. +- Browser-only values captured during server render. +- Switching the entire component only because `window` is absent. +- Rich transient tooltip controls entering tab order before pinning. +- Missing cleanup from application-owned overlays outside the adapter. + +Source: `docs/framework/react/adapter.md`; `docs/framework/react/reference/chart.md` diff --git a/packages/charts-core/skills/ship-accessible-charts/references/renderers-and-export.md b/packages/charts-core/skills/ship-accessible-charts/references/renderers-and-export.md new file mode 100644 index 00000000..0c92a881 --- /dev/null +++ b/packages/charts-core/skills/ship-accessible-charts/references/renderers-and-export.md @@ -0,0 +1,42 @@ +# Renderers and Export + +## Renderer Decision + +| Requirement | Choose | Tradeoff to verify | +| --------------------------------------------------- | ---------------------------- | -------------------------------------------------------------------- | +| Visible server geometry, vector output, DOM styling | default SVG | scene node count and DOM interaction cost | +| Large paint workload, raster-first presentation | Canvas | first pixels after client mount; no vector/accessibility descendants | +| Deterministic server or file output | `renderChartSvg` | explicit dimensions, text metrics, resource IDs | +| Spring/timing cascade | injected `motion()` renderer | bundle, reduced motion, interruption | +| Native application | React Native SVG host | device, typography, gestures, accessibility, Metro | +| Custom platform | renderer/host extension | scene contract, lifecycle, fallback, cleanup | + +## Export Decision + +| Need | Path | +| ------------------------------------------ | ------------------------------------------------ | +| DOM-free SVG from a definition/scene | `createChartScene` or runtime + `renderChartSvg` | +| Mounted SVG with computed browser styles | `serializeChartSvg` / `downloadChartSvg` | +| PNG, JPEG, or WebP from mounted SVG/Canvas | `renderChartImage` / `downloadChartImage` | +| Raw Canvas base bitmap only | selected Canvas surface bitmap APIs | + +## Artifact Policy + +- Set explicit output dimensions; browser responsiveness is not a file-size policy. +- Set theme and raster background for the destination medium. +- Scope gradients/clips with `idPrefix` when artifacts share a document. +- Decide whether focused/selected presentation belongs in the artifact. +- Embed or inline fonts, images, and CSS when the consumer cannot reach application resources. +- Give the exported state its own meaningful name and description. +- Pair Canvas or static artifacts with text/table context when interaction was essential. + +## Lifecycle Verification + +1. Server/static render has no DOM dependency. +2. Browser host adopts or paints the intended surface. +3. Resize preserves semantic domains and current state. +4. Renderer replacement cleans up its previous host controls. +5. Export after update/focus includes exactly the intended scene layers. +6. Destroy releases observers, listeners, tooltip mounts, animation frames, and renderer resources. + +Source: `docs/guides/exporting.md`; `docs/guides/ssr-and-hydration.md`; `docs/reference/rendering-and-export.md` diff --git a/packages/charts-core/skills/ship-accessible-charts/references/solid.md b/packages/charts-core/skills/ship-accessible-charts/references/solid.md new file mode 100644 index 00000000..f3e62d18 --- /dev/null +++ b/packages/charts-core/skills/ship-accessible-charts/references/solid.md @@ -0,0 +1,22 @@ +# Solid Adapter + +Use `Chart` from `@tanstack/charts/solid`. Build definitions that capture signals with `createMemo` and pass the memoized value. + +## Ownership + +- The adapter prerenders from reactive props, mounts in `onMount`, updates from effects, and destroys in `onCleanup`. +- Keep behavior in the definition and surface/accessibility concerns on `Chart`. +- Use `renderTooltipBody` for Solid-owned content. Read the tooltip context reactively instead of destructuring it and losing tracking. + +## SSR + +Solid emits the complete accessible SVG. `initialWidth` controls server geometry, and `createUniqueId()` requires deterministic server/client trees. + +## Failure Checks + +- Calling a definition factory repeatedly instead of memoizing captured values. +- Destructuring reactive tooltip context and freezing content. +- Browser-only measurements in definition construction. +- Controls rendered before `tooltip.pinned` is true. + +Source: `docs/framework/solid/adapter.md`; `docs/framework/solid/reference/chart.md` diff --git a/packages/charts-core/skills/ship-accessible-charts/references/svelte.md b/packages/charts-core/skills/ship-accessible-charts/references/svelte.md new file mode 100644 index 00000000..eb6dd5f2 --- /dev/null +++ b/packages/charts-core/skills/ship-accessible-charts/references/svelte.md @@ -0,0 +1,22 @@ +# Svelte Adapter + +Use `Chart` from `@tanstack/charts/svelte`. Derive the complete definition with Svelte 5 `$derived` when it captures reactive values. + +## Ownership + +- The adapter prerenders through the shared controller, mounts in `onMount`, updates from an effect, and returns cleanup that destroys the host. +- Keep definition behavior in the definition and surface/accessibility options on `Chart`. +- Pass a Svelte 5 `tooltipBody` snippet for framework-owned content; the host owns placement, pinning, portaling, and dismissal. + +## SSR + +Svelte emits the complete accessible SVG. `initialWidth` controls server geometry, and `$props.id()` requires the same deterministic tree, values, and formatting at hydration. + +## Failure Checks + +- Rebuilding a captured definition during unrelated template work instead of deriving it. +- Reading browser size during server construction. +- Treating snippet content as tooltip behavior. +- Interactive transient content before the tooltip is pinned. + +Source: `docs/framework/svelte/adapter.md`; `docs/framework/svelte/reference/chart.md` diff --git a/packages/charts-core/skills/ship-accessible-charts/references/vue.md b/packages/charts-core/skills/ship-accessible-charts/references/vue.md new file mode 100644 index 00000000..82befc94 --- /dev/null +++ b/packages/charts-core/skills/ship-accessible-charts/references/vue.md @@ -0,0 +1,22 @@ +# Vue Adapter + +Use `Chart` from `@tanstack/charts/vue`. Build captured definitions with `computed`; module-scope definitions need no reactive wrapper. + +## Ownership + +- The adapter prerenders through the shared controller, mounts in `onMounted`, updates from complete props, and destroys in `onBeforeUnmount`. +- Definition behavior stays in the definition. +- Use the scoped `#tooltipBody` slot for Vue-owned body content and read its pinned/dismiss state. + +## SSR + +Vue emits the complete accessible SVG. `initialWidth` determines server geometry, and `useId()` remains stable when the same tree, data, formatters, and dimensions render on server and client. + +## Failure Checks + +- Constructing a definition in template evaluation without `computed`. +- Mutating a definition or transformed rows in place and expecting update discovery. +- Divergent locale/time/random output during hydration. +- Slot controls present while the tooltip is transient rather than pinned. + +Source: `docs/framework/vue/adapter.md`; `docs/framework/vue/reference/chart.md` diff --git a/packages/charts-core/skills/update-and-animate-charts/SKILL.md b/packages/charts-core/skills/update-and-animate-charts/SKILL.md new file mode 100644 index 00000000..0f165fe5 --- /dev/null +++ b/packages/charts-core/skills/update-and-animate-charts/SKILL.md @@ -0,0 +1,164 @@ +--- +name: update-and-animate-charts +description: > + Update TanStack Charts through stable definition and datum identity, + configure SVG motion, and preserve correctness during resize, + interruption, streaming, and rolling windows. Load for reactive data, + animation, reordering, enter/exit, or stale interaction state. +metadata: + type: core + library: '@tanstack/charts' + library_version: '0.9.0' +sources: + - 'TanStack/charts:docs/guides/dynamic-data-and-animation.md' + - 'TanStack/charts:docs/reference/motion.md' + - 'TanStack/charts:docs/reference/chart-definitions.md' +--- + +# Update and Animate Charts + +Use **trigger → inspect → decide → build → verify**. Definition identity is the application update boundary; mark and datum identities determine what survives it. + +## Setup + +Create a new definition only when captured data or visual policy changes, then update the host with that definition: + +```ts +import { barX, defineChart, mountChart } from '@tanstack/charts' +import { scaleBand } from '@tanstack/charts/scales/band' +import { scaleLinear } from '@tanstack/charts/scales/linear' + +interface Row { + id: string + label: string + value: number +} + +function createRanking(rows: readonly Row[]) { + const ranked = [...rows].sort((left, right) => right.value - left.value) + + return defineChart({ + svgAnimation: { duration: 280, easing: 'ease-out' }, + marks: [barX(ranked, { id: 'ranking', x: 'value', y: 'label', key: 'id' })], + x: { scale: scaleLinear, nice: true }, + y: { scale: () => scaleBand().padding(0.1) }, + }) +} + +const element = document.querySelector('#ranking') +if (!element) throw new Error('Missing #ranking') + +const firstRows: readonly Row[] = [ + { id: 'core', label: 'Core', value: 82 }, + { id: 'react', label: 'React', value: 74 }, +] + +const options = { + definition: createRanking(firstRows), + height: 280, + ariaLabel: 'Package ranking', +} + +export const host = mountChart(element, options) + +export function updateRanking(rows: readonly Row[]) { + host.update({ ...options, definition: createRanking(rows) }) +} +``` + +Framework adapters use their native memoization primitive around the complete definition. + +## Core Patterns + +### Stabilize three identities + +1. **Definition identity** — stable until captured values change. +2. **Mark `id`** — stable across conditional layers and reorder. +3. **Datum `key`** — stable entity identity, independent of row position or mutable metrics. + +Verify that focus, selection, tooltip pinning, and exit motion follow the semantic entity after reorder. + +### Choose the smallest motion contract + +- `svgAnimation: true` for lightweight keyed SVG tweening. +- `svgAnimation` options for duration, easing, and reduced-motion policy. +- `motion()` renderer only when spring continuity, a timing cascade, or rolling path behavior is part of the product contract. +- No animation for static export, server output, or changes where transition would imply false continuity. + +Resize animation defaults off. Keep it off for ordinary observed containers. + +### Bound streaming work + +Keep source history outside the chart, pass a bounded visible window, preserve keys for retained samples, keep viewport state controlled, and coalesce upstream updates when only the latest state matters. + +### Verify interruption, not only endpoints + +Apply an update during enter, update, exit, focus motion, resize, and rolling movement. The latest accepted definition must win; focused or selected semantic identity must not jump to another row. + +## Common Mistakes + +### CRITICAL Creating a fresh definition every render + +Wrong: call the definition factory during every unrelated application render. + +Correct: keep the definition stable until a captured row or visual-policy value changes; use the framework's native memoization primitive or update it deliberately in a vanilla owner. + +Unnecessary identity changes invalidate work and can reset presentation state. + +Source: definition-identity migration in `CHANGELOG.md`; `docs/guides/dynamic-data-and-animation.md` + +### CRITICAL Keying entities by row position + +Wrong: + +```ts +barX(rows, { x: 'value', y: 'name', key: (_row, index) => index }) +``` + +Correct: + +```ts +barX(rows, { x: 'value', y: 'name', key: 'id' }) +``` + +Insertion, deletion, and reorder retarget geometry, focus, and exit motion when index is identity. + +Source: `API-FRICTION.md` F-131, F-239; `docs/guides/dynamic-data-and-animation.md` + +### HIGH Animating every responsive resize + +Wrong: + +```ts +svgAnimation: { + resize: true +} +``` + +Correct: + +```ts +svgAnimation: true +``` + +Container observation can repeatedly restart transitions and leave layout behind the actual panel. + +Source: `API-FRICTION.md` F-129; `docs/guides/responsive-charts.md` + +### HIGH Morphing rolling samples by index + +Wrong: key a shifting time window by array index. + +Correct: key each observation by stable timestamp or event ID and let removed/added samples exit and enter at the window edges. + +Index identity turns old times into different samples instead of preserving retained observations. + +Source: `API-FRICTION.md` F-240; `docs/guides/dynamic-data-and-animation.md` + +### HIGH Tension: motion continuity versus current-state correctness + +Visual continuity cannot make stale state acceptable. Verify rapid retargeting, interrupted exits, active focus, and resize while motion is in flight. + +See also: `design-responsive-charts/SKILL.md` and `debug-and-verify-charts/SKILL.md` + +See also: `build-chart-interactions/SKILL.md` and `coordinate-charts-with-tanstack/SKILL.md` — stable mark and datum identity preserves controlled interaction across local, synchronized, and optimistic updates. diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index a39c5a1e..31744f11 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -675,6 +675,9 @@ importers: specifier: '>=3.5' version: 3.5.40(typescript@6.0.3) devDependencies: + '@tanstack/intent': + specifier: ^0.3.6 + version: 0.3.6 '@types/d3-array': specifier: ^3.2.2 version: 3.2.2 @@ -3816,6 +3819,13 @@ packages: svelte: ^5.46.4 vite: ^8.0.0-beta.7 || ^8.0.0 + '@tanstack/intent@0.3.6': + resolution: + { + integrity: sha512-ylew/4T3layUXSfE/SDGUSAi1B3U/wuoNUE0adC3j/s6wzX7omV6e6hAv0WyMC52Inren8kqvwpHj+G/ZBbQHQ==, + } + hasBin: true + '@tsrx/core@0.1.48': resolution: { @@ -4735,6 +4745,13 @@ packages: } engines: { node: '>= 0.8' } + cac@6.7.14: + resolution: + { + integrity: sha512-b6Ilus+c3RrdDk+JhLKUAQfzzgLEPy6wcXqS7f/xe1EETvsDP6GORG7SFuOs6cID5YkqchW/LXZbX5bc8j7ZcQ==, + } + engines: { node: '>=8' } + call-bind-apply-helpers@1.0.2: resolution: { @@ -11640,6 +11657,14 @@ snapshots: vite: 8.1.5(@types/node@25.9.5)(esbuild@0.28.1)(less@4.8.1(supports-color@10.2.2))(sass@1.102.0)(terser@5.49.0)(yaml@2.9.0) vitefu: 1.1.3(vite@8.1.5(@types/node@25.9.5)(esbuild@0.28.1)(less@4.8.1(supports-color@10.2.2))(sass@1.102.0)(terser@5.49.0)(yaml@2.9.0)) + '@tanstack/intent@0.3.6': + dependencies: + cac: 6.7.14 + jsonc-parser: 3.3.1 + semver: 7.8.5 + std-env: 4.2.0 + yaml: 2.9.0 + '@tsrx/core@0.1.48': dependencies: '@jridgewell/sourcemap-codec': 1.5.5 @@ -12234,6 +12259,8 @@ snapshots: bytes@3.1.2: {} + cac@6.7.14: {} + call-bind-apply-helpers@1.0.2: dependencies: es-errors: 1.3.0