Skip to content

fix(charts): scope focus guide gradient paints with idPrefix - #217

Open
getdownandcode wants to merge 1 commit into
TanStack:mainfrom
getdownandcode:fix/focus-guide-id-prefix
Open

getdownandcode wants to merge 1 commit into
TanStack:mainfrom
getdownandcode:fix/focus-guide-id-prefix

Conversation

@getdownandcode

@getdownandcode getdownandcode commented Oct 13, 2026 •

Copy link
Copy Markdown

Fixes #211

What changed & why

When a chart is configured with idPrefix, <defs> gradients are scoped (e.g. <linearGradient id="chart-a-fade">), and renderChartSvg rewrites mark paints via resolvePaint. However, transient focus guides rendered via renderFocusGuideLayer used default hooks without resolvePaint, leaving url(#id) references unprefixed on hover:

- <line class="ts-chart-focus-guide" stroke="url(#fade)" ... />
+ <line class="ts-chart-focus-guide" stroke="url(#chart-a-fade)" ... />

Changes

  • Added createFocusGuideRenderHooks(scene) in svg-focus-guide-layer.ts to scope url(#id) paints against scene.gradients (returns undefined when the scene has no gradients).
  • Cached the hooks per scene update in svg-surface.ts and motion.ts so no Set or closure allocations happen during high-frequency pointer hover events.
  • Left static SVG document rendering (svg.ts / svg-renderer.ts) untouched, preserving isolated static SVG bundle boundaries.
  • Updated universal-baseline.json and adjusted 7 derivative bundle ceilings in scripts/measure-bundles.mjs to reflect the small helper cost (~70 B gzip on DOM host).
  • Added regression tests in svg-surface.test.ts and motion.test.ts.
  • Added a patch changeset for @tanstack/charts.
Validation output
✓ packages/charts-core/src/svg-surface.test.ts (14 tests)
✓ packages/charts-core/src/motion.test.ts (66 tests)
Test Files  2 passed (2)
Tests  80 passed (80)

Bundle size policy passed.
Framework package gate passed for 7 adapters.
Unified @tanstack/charts artifact gate passed.

Summary by CodeRabbit

  • Bug Fixes
    • Focus guides and crosshairs now correctly resolve gradient paint references when an ID prefix is configured. This keeps their appearance consistent in standard and animated charts, including when the prefix contains characters that need to be normalized for SVG references.

@coderabbitai

coderabbitai Bot commented Oct 13, 2026 •

Copy link
Copy Markdown

Review in Change Stack →

No actionable comments were generated in the recent review.

Recent review info
Run configuration
  • Source mode: Auto
  • Repository: Unavailable
  • Branch: Unavailable
  • File: No configuration file from this repository applied
  • Commit: Unavailable
  • Layers: defaults
  • Review profile: CHILL
  • Plan: Advanced
  • Run ID: a554111d-cad4-468e-b922-a38cfe24c2b1
Commits

Reviewing files that changed from the base of the PR and between ae1703f and 3046be0.

Files selected for processing: 2
  • packages/charts-core/src/motion.test.ts
  • packages/charts-core/src/svg-surface.test.ts

Included review availability: This review used your included allowance. Your plan provides up to 4 included reviews per hour; 3 remain after this review.

Walkthrough

Focus-guide and crosshair paint references now use scene gradient IDs when an SVG render has an idPrefix. The SVG surface and motion renderer create and pass render hooks for focus-guide painting. Tests cover prefixed gradient references, including prefixes with punctuation.

Changes: Focus-guide paint scoping

Layer / File(s) Summary
Create hooks and wire the SVG surface
packages/charts-core/src/svg-focus-guide-layer.ts, packages/charts-core/src/svg-surface.ts, packages/charts-core/src/svg-surface.test.ts, .changeset/focus-guide-id-prefix-paint.md
The hook factory rewrites matching gradient references with a sanitized idPrefix. The SVG surface passes the hooks to the default focus-guide renderer. The test checks a prefixed crosshair gradient reference.
Pass hooks through motion rendering
packages/charts-core/src/motion.ts, packages/charts-core/src/motion.test.ts
The motion renderer creates and passes hooks during scene rendering and focus painting. The test checks that a focus-guide stroke uses the prefixed gradient ID.

Priority: Normal

Estimated code review effort: 2 (Simple) | ~10 minutes

Change: Bug fix · Severity of issue fixed: Medium

Possibly related issues

  • Issue 191 — The change addresses its focus-guide paint-scoping portion. Its gradient-stop and gradientUnits requirements remain separate.

Suggested reviewers: tannerlinsley

Merge Risk: Minimal · up to 3046b

No actionable merge-blocking risk is established in this change.

Pre-merge checks: Passed 4 · Failed 1

Failed Failed checks: 1 warning

Check name Status Explanation Resolution
Docstring Coverage Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Docstring coverage is scoped to functions touched by this diff. Analyzed 6 functions across 6 files. Write docstrings for the functions missing them to satisfy the coverage threshold.
Passed Passed checks: 4 passed
Check name Status Explanation
Description Check Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check Passed The title clearly and concisely describes the main change: scoping focus-guide gradient paints with idPrefix.
Linked Issues check Passed Issue #211 requires guide and crosshair paints that use url(#id) to resolve to the idPrefix-scoped gradient id, matching mark paints. The head adds createFocusGuideRenderHooks(scene) in svg-focus-guid…
Out of Scope Changes check Passed The source changes connect directly to #211. createFocusGuideRenderHooks, the hook wiring in svg-surface.ts and motion.ts, and the regression tests in svg-surface.test.ts and motion.test.ts all implem…
Full details: Linked Issues check

Explanation

Issue #211 requires guide and crosshair paints that use url(#id) to resolve to the idPrefix-scoped gradient id, matching mark paints. The head adds createFocusGuideRenderHooks(scene) in svg-focus-guide-layer.ts. It rewrites url(#id) only when id is in scene.gradients. It sanitizes idPrefix with the same rule that renderChartSvg uses in svg.ts (remove characters outside [a-zA-Z0-9_-]), so guide and mark ids match. svg-surface.ts and motion.ts create these hooks on each scene update and pass them to renderFocusGuideLayer in the default SVG path. The reported tests cover a crosshair stroke that resolves to url(#a-fade) and a sanitized prefix case. Pattern resources are described in #211 as future work after PR #166, so their absence is not an unmet requirement. Residual uncertainty: the renderer-based guide path (renderFocusGuideLayerWithRenderer) is not changed. The source evidence does not show whether that path already scopes paints through the supplied renderer. The reported tests and maintainer checks cover both renderers, but the test files were not executed in this review.

Full details: Out of Scope Changes check

Explanation

The source changes connect directly to #211. createFocusGuideRenderHooks, the hook wiring in svg-surface.ts and motion.ts, and the regression tests in svg-surface.test.ts and motion.test.ts all implement or verify the scoped focus-guide paint behavior. The changeset documents the fix. Static SVG document rendering (svg.ts, svg-renderer.ts) is not changed, as the PR description states. The PR description also reports updates to universal-baseline.json and 7 bundle ceilings in scripts/measure-bundles.mjs for the helper cost. Those files do not appear in the file-level change summary, so their presence at head was not verified. If present, they are tied to the new helper, but the maintainer states that bundle-limit increases are a separate approval gate. Those bundle-limit changes should be reported separately from this fix and are not evaluated as a product behavior change here.

Finishing Touches
🧪 Generate unit tests (beta)
  • Create a new PR
  • Autofix · Keep fixing CodeRabbit findings and required CI, and resolving merge conflicts

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@changeset-bot

changeset-bot Bot commented Oct 13, 2026 •

Copy link
Copy Markdown

🦋 Changeset detected

Latest commit: 3046be0

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 12 packages
Name Type
@tanstack/charts Patch
@tanstack/alpine-charts Patch
@tanstack/angular-charts Patch
@tanstack/lit-charts Patch
@tanstack/octane-charts Patch
@tanstack/preact-charts Patch
@tanstack/react-charts Patch
@tanstack/react-native-charts Patch
@tanstack/solid-charts Patch
@tanstack/svelte-charts Patch
@tanstack/vue-charts Patch
@tanstack/charts-scales Patch

Not sure what this means? Click here to learn what changesets are.

Click here if you're a maintainer who wants to add another changeset to this PR

@tannerlinsley

Copy link
Copy Markdown
Member

Thanks @getdownandcode, I verified the gradient fix at ae1703f. All 72 cases passed in Chromium, Firefox and WebKit, covering both SVG renderers, sanitized prefixes, gradient removal and re-addition, marker fills, and unrelated paint references. The same checks reproduce 12 failures per browser on main.

The remaining concern is bundle cost. A paired check of the changed runtime files across 114 entry points found increases up to 0.095 kB gzip and 0.096 kB Brotli. This is a targeted check, not the full repository validation. The proposed budget increases still need approval before merging.

I also tried sharing the existing SVG paint resolver. That reduced the maximum gzip increase to 0.048 kB, but affected more entry points and increased the worst Brotli delta to 0.114 kB, so I haven't applied that experiment to your PR. Keeping #211 open until the fix clears the remaining checks and lands.

@getdownandcode

Copy link
Copy Markdown
Author

@tannerlinsley Thanks for verifying the tests and bundle size! Glad all 72 cases passed. I'll let the remaining checks finish—let me know if you need any other changes before this lands.

@tannerlinsley

tannerlinsley commented Oct 16, 2026 •

Copy link
Copy Markdown
Member

Thanks @getdownandcode, the isolated checkout and package checks below are complete at ae1703f. All 2,214 unit tests and 23 tests across the six additional framework configurations pass, along with TypeScript and the docs contract. The packed-package gate also passes, including bare React Native, Expo, seven adapters and the unified artifact.

I reran the 72 focus-guide cases against the final packed exports in Chromium, Firefox and WebKit. All pass, covering sanitized prefixes, gradient removal and re-addition, marker fills and unrelated paint references. The negative control fails as expected.

The bundle check passes against this PR’s proposed limits, but that does not approve those increases. The budget decision and required CI remain separate gates, so #211 stays open until the fix lands. No limits were changed during this verification.

Remaining repository checks: the catalog-preview fingerprint is stale at this head. I rendered all 188 cases in both themes from the isolated production build and regenerated their portable SVGs. Every SVG is byte-identical to the committed asset, so only manifest sourceHash needs to become 84b32be4f1786eb915ea8c9bea859339705ce3df101ec090924db31031b1f4b9. That local correction passes the preview gate. The native declarations, catalog index/examples and conformance build also pass. The full comparison check fails only on its recorded input fingerprint (8444bc4f… versus measured 6afbd8b3…), not size tolerances. I have not changed comparison records or budgets. These results are not an all-CI-green claim.

Verified head: ae1703f46a1042fec0c3f657c4ce9d7140a5d4cb.

@getdownandcode

Copy link
Copy Markdown
Author

Makes total sense on keeping the budget decision as a separate gate. Really appreciate you running such thorough checks across the full test suite and packed exports. @tannerlinsley, Happy to hold tight until the team signs off on it.

@getdownandcode

Copy link
Copy Markdown
Author

Hey @tannerlinsley, noticed some recent merges on main touched the bundle baselines. Let me know if you'd like me to rebase onto latest main!

@tannerlinsley

Copy link
Copy Markdown
Member

Thanks @getdownandcode, yes please rebase onto current main. Keep the fix scoped to the focus-guide paints, and report any bundle-limit changes separately so we can review the remaining cost against the new base. The earlier budget and CI holds still apply.

Scope focus guide and crosshair url(#id) gradient paint references when an idPrefix is configured on both static SVG and motion surfaces.

Fixes TanStack#211
@getdownandcode
getdownandcode force-pushed the fix/focus-guide-id-prefix branch from ae1703f to 3046be0 Compare October 25, 2026 08:30
@getdownandcode

Copy link
Copy Markdown
Author

@tannerlinsley Rebased onto current main at 3046be05 with the commit strictly scoped to the focus-guide paint changes and tests (leaving baseline/budget files untouched). All unit tests and release package gates (pnpm package:check) pass cleanly.

Here is the measured bundle delta against the new base:

Locked Universal Baselines:

  • TanStack DOM host: +285 B raw, +77 B gzip (28.44 kB → 28.51 kB)
  • React adapter: +285 B raw, +80 B gzip (29.37 kB → 29.45 kB)
  • React line consumer: +285 B raw, +63 B gzip (38.50 kB → 38.56 kB)
  • React compact-scale line consumer: +287 B raw, +96 B gzip (31.55 kB → 31.64 kB)

Derivative Entry Point Ceilings:

  • React compact-scale line consumer: 31.64 kB (budget: 31.55 kB, +0.09 kB)
  • React line consumer + mark Canvas renderer: 45.63 kB (budget: 45.55 kB, +0.08 kB)
  • Direct D3 quadtree + TanStack DOM host: 39.93 kB (budget: 39.85 kB, +0.08 kB)
  • Motion SVG renderer: 22.77 kB (budget: 22.70 kB, +0.07 kB)
  • Stats parity surface: 57.44 kB (budget: 57.37 kB, +0.07 kB)
  • Direct D3 Delaunay + TanStack DOM host: 45.19 kB (budget: 45.14 kB, +0.05 kB)
  • React Stats parity surface: 58.51 kB (budget: 58.46 kB, +0.05 kB)

Static SVG document rendering (svg.ts / svg-renderer.ts) remains 100% untouched with 0 byte change.

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Bug] Focus guides: url(#id) paints ignore idPrefix

2 participants