SyncGroup aligns the data — shared cursor, shared x-range — but nothing sets axes[].size, so uPlot sizes each axis to its widest tick label.
Two stacked, synced charts therefore put the same instant at different screen x, and drift as the labels change width (1.6 → -24.20). A chart with a right-hand axis also runs an axis-width narrower than one without.
This is the blocker that is easiest to miss, because SyncGroup looks like it should already cover it.
Gotcha for whoever implements it: uPlot adds axis.labelSize (default 30) on top of axis.size for any axis whose label is non-null, and reserves nothing when label is null. So a gutter is size + labelSize + padding, padding standing in for an absent axis must total the same, and every axis needs a label ("" counts). Getting this wrong leaves charts misaligned by exactly 30 px.
SyncGroupaligns the data — shared cursor, shared x-range — but nothing setsaxes[].size, so uPlot sizes each axis to its widest tick label.Two stacked, synced charts therefore put the same instant at different screen x, and drift as the labels change width (
1.6→-24.20). A chart with a right-hand axis also runs an axis-width narrower than one without.This is the blocker that is easiest to miss, because
SyncGrouplooks like it should already cover it.Gotcha for whoever implements it: uPlot adds
axis.labelSize(default 30) on top ofaxis.sizefor any axis whoselabelis non-null, and reserves nothing whenlabelis null. So a gutter issize + labelSize + padding, padding standing in for an absent axis must total the same, and every axis needs a label (""counts). Getting this wrong leaves charts misaligned by exactly 30 px.