`motion()` renderer: the tooltip follows the 1.1 s data transition, and mark `states` wait for the entrance motion
Maintainers usually reply within 1 day
Nobody has claimed this yet.
Assessment
- Difficulty
- 4/5
- Estimated time
- 3-5 days
- Newbie friendliness
- 52/100
- Issue type
- Bug
- Clarity
- Mostly clear
- Activity status
- Active
- Tech stack
- typescript
- Domain
- data-visualization
Research direction
Start with dist/motion.js and the “Tooltip motion” section in docs/guides/tooltips-and-focus.md; reproduce the two pointer behaviors using the supplied chart example. Check how tooltip motion and pendingStateFocus are handled during data transitions. Done means tooltip movement no longer inherits the slow data transition by default and focus states behave during entrance motion, or the states reference clearly documents any necessary deferral.
Written by the indexing model from the issue text.
Description
Version: @tanstack/charts 1.0.0, motion() from @tanstack/charts/motion
Problem
We use the motion() renderer for the entrance and data-update animations. Two default behaviors made the charts feel slow to the pointer.
(a) The tooltip moves with the data transition
dist/motion.js sets defaultDuration = 1100. The tooltip uses the same transition for entry, movement between points and exit. This is documented in "Tooltip motion" in docs/guides/tooltips-and-focus.md.
When the pointer moves from one point to the next, the tooltip takes 1.1 s to arrive. A transition of 1.1 s is good for the data, but it is too slow for a pointer. Each of our charts with a tooltip needed tooltip.motion: false.
(b) Mark states do not apply during the entrance motion
While the data motion runs, paintFocus keeps the state focus in pendingStateFocus. The renderer applies it only when the data motion ends. With the default duration and stagger, the entrance takes about 2 s on a chart with some data.
If the pointer is on the chart during the entrance, the tooltip shows, but focus states (for example a dot that grows on focus) do not apply until the entrance ends. The pointer looks broken for that time.
Minimal repro
import { defineChart, dot, lineY } from '@tanstack/charts';
import { motion } from '@tanstack/charts/motion';
import { RendererChart } from '@tanstack/charts/react/tooltip';
import { scaleLinear } from '@tanstack/charts/scales/linear';
import { tooltip } from '@tanstack/charts/tooltip';
const rows = Array.from({ length: 30 }, (_, x) => ({ x, y: Math.sin(x / 3) }));
const renderer = motion();
const definition = defineChart({
marks: [
lineY(rows, { x: 'x', y: 'y' }),
dot(rows, {
x: 'x',
y: 'y',
r: 2,
states: [{ when: { focus: 'x' }, style: { r: 6 } }],
}),
],
scales: { x: { scale: scaleLinear }, y: { scale: scaleLinear } },
focus: 'nearest-x',
tooltip,
});
export const Repro = () => (
<RendererChart
definition={definition}
renderer={renderer}
ariaLabel="Repro"
width={600}
height={200}
/>
);
- Load the page with the pointer over the chart. The dot under the pointer does not grow until the entrance ends.
- After the entrance, move the pointer fast from left to right. The tooltip lags behind by about 1 s.
Expected
- The tooltip follows the pointer with a short transition by default (for example 100 to 200 ms), or with no transition.
- Focus
statesapply at once, also during the entrance motion. Or, if the deferral is necessary, the docs say so.
Actual
- The tooltip uses the 1100 ms data transition.
- Focus
stateswait until the data motion ends.
Our workaround
Our shared chart wrapper sets tooltip.motion: false when the definition does not set one:
// The motion renderer also moves the tooltip to each new point over 1.1s. Keep
// the tooltip at the point at once, unless the definition sets its own motion.
function withImmediateTooltip(definition) {
const { tooltip } = definition;
if (!tooltip) return definition;
const config = 'create' in tooltip ? { use: tooltip } : tooltip;
if (config.motion !== undefined) return definition;
return { ...definition, tooltip: { ...config, motion: false } };
}
We have no workaround for (b).
Suggested fix
- Give the tooltip its own, shorter default transition. Keep the current
tooltip.motionoverride. - Apply focus
statesat once while the data motion runs, or let the state transition retarget from the current frame. If this is not possible, document the deferral in thestatesreference.
Written by an agent (Claude Code, claude-opus-5-5).
- Dominant language
- TypeScript
- Stars
- 819
- Forks
- 49
- Avg merge
- 9h 37m
- Merged PRs (30d)
- 8
Getting set up
- No Dockerfile or Docker Compose file
- No pull request template
- Read the contributing guide
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
More from TanStack/charts
-
Renderer treats a sub-pixel width difference as a resize, which cancels the `motion()` entranceOpen
Difficulty 1/5 Under an hour Newbie friendliness 85/100
TanStack/charts#182 · 1 comment ·
Maintainers usually reply within 1 day
-
brushX with `values` can shorten the selection range when it’s dragged to the plot edgePossibly taken @andersk claimed this 4 days ago. Open
Difficulty 2/5 1-3 hours Newbie friendliness 78/100
Maintainers usually reply within 1 day
-
Difficulty 5/5 Over a week Newbie friendliness 35/100
Maintainers usually reply within 1 day
-
Grid and axis lines have no `shape-rendering` option, so hairlines anti-alias across two device rowsOpen
Difficulty 3/5 1-2 days Newbie friendliness 76/100
Maintainers usually reply within 1 day
-
Difficulty 4/5 3-5 days Newbie friendliness 35/100
Maintainers usually reply within 1 day
Similar issues
-
submodule-pointer-regression
Difficulty 1/5 Under an hour Newbie friendliness 72/100
smith-horn/skillsmith#3061 ·
Maintainers usually reply within 1 day
-
area: ops type: test
Difficulty 2/5 1-3 hours Newbie friendliness 79/100
accensa/x402-facilitator-stellar#559 ·
Maintainers usually reply within 1 day
-
documentation
Difficulty 2/5 1-3 hours Newbie friendliness 74/100
cosimochellini/one-piece-zero-spoiler#551 ·
Maintainers usually reply within 1 day
-
getWatched() omits __proto__ directories when cwd is setPossibly taken @maxazure claimed this today. Open
Difficulty 2/5 1-3 hours Newbie friendliness 79/100
-
area:web enhancement
Difficulty 2/5 1-3 hours Newbie friendliness 84/100
Maintainers usually reply within 1 day