inset strain labels from the chart edge and add a spacing parameter - #9
Merged
Conversation
With `connect_leader_to_label` on, the strain labels were anchored exactly at `chart_edge_branch` — the tree panel's chart-facing edge — and right-aligned, so the text ran up against the chart's frame and the frame line stopped reading as a line. `label_pad_px` was already counted into the label strip's width, but because the anchor sat on the edge that pad landed on the tree side of the text rather than between the text and the chart. Reserve a pad at each end of the strip and anchor the labels one pad inside the chart-facing edge, with the leader lines stopping at the same place so they meet the text instead of passing under it to the panel edge. The tree panel grows by one pad, so a hand-tuned `shift_tree_loc` may want the same adjustment. Separately, `_concat_for_location` hardcoded `spacing=0` at all four `tree_location` branches, leaving callers no way to open a gap between the panels. Expose it as `spacing`, defaulting to 0 so existing charts are unchanged. It is easy to confuse with `shift_tree_loc`, which moves the tree within its own panel by resizing the label strip, so the two descriptions now cross-reference each other. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
jbloom
force-pushed
the
label-inset-and-spacing
branch
from
August 20, 2026 20:28
b32302e to
3a0d348
Compare
The inset reused `label_pad_px`, which clears the deepest tip, for the gap that clears the chart's frame. Those are different jobs and want different sizes: at the font sizes in practice the pad is 4px, which reads as more separation than the frame needs. Add `_LABEL_CHART_GAP_PX_MIN` / `_LABEL_CHART_GAP_RATIO`, giving `max(2, font_size * 0.2)` -- half the tree-facing pad at every font size, so 2px at fonts 9 and 10. The tree panel now grows by that instead of by a full pad, halving the layout shift the inset introduces. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2px left the labels reading as slightly too close to the chart's frame. Raise the floor to 3px; the ratio is unchanged, so it only differs below font size 15. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
The bug
With
connect_leader_to_labelon, the strain labels are anchored exactly atchart_edge_branch— the tree panel's chart-facing edge — and right-aligned, so the textruns up against the chart's frame and the frame line becomes hard to read.
A pad was already being counted into the label strip's width:
but because the anchor sits on the edge, that pad lands on the tree side of the text and
none of it between the text and the chart. The strip reserved room for a gap that was never
drawn.
Spotted on the flu-seqneut-2026 titer charts, where the long haplotype labels make it
obvious.
The fix
The gap that clears the chart's frame is a different job from the pad that clears the
deepest tip, so it gets its own constant rather than reusing
label_pad_px:The strip now reserves both, and the labels anchor
chart_gap_pxinside the edge:The leader lines stop at the same place, so they meet the text rather than passing under it
to the panel edge.
per_pixelis the right converter: the panel spanstree_size + strip_pixel_widthpixels over a proportionally extended branch domain, sobranch-units-per-pixel stays
branch_span / tree_sizethroughout.Because the anchor is a data value on the branch scale and each layout already orders its
own
branch_domain, this works unchanged for all fourtree_locationvalues.The 3px floor was chosen by rendering the affected charts at 2, 3 and 4px and comparing;
2px still read as crowded against the frame, 4px as loose.
Behavior change: the tree panel grows by
max(3, strain_label_font_size * 0.2)px.Since
strip_pixel_width = label_pixel_width - shift_tree_loc, anyone who hand-tunedshift_tree_locmay want the same adjustment. Noted in the CHANGELOG.The new parameter
_concat_for_locationhardcodedspacing=0at all four branches, leaving callers no wayto open a gap between the panels — Vega-Lite's own concat default is 20, so 0 was a
deliberate "tree attached to chart" choice, just not an overridable one.
spacingnow exposes it, defaulting to 0 so existing charts are unchanged. The insetabove is what fixes the flush-label problem;
spacingis a general layout knob, not aworkaround for it. Negative values are accepted and overlap the panels.
It is easy to confuse with
shift_tree_loc, which is described as moving the tree"toward/away from the chart" but acts inside the tree panel by resizing the label strip.
The two descriptions now cross-reference each other.
Per the single-source-of-truth design,
cli.pyand the docs needed no edits —--spacingand the rendered API page generate from the
PlotConfigfield. Verified both.Tests
231 pass, up from 218.
test_tree_location.py—spacingdefaults to 0, propagates, and holds across all fourtree_locationvalues; panel dimensions are unaffected.test_label_connection.py— labels sit strictly insidechart_edge_branchin bothorientations, and leader
x2equals the label anchor.test_appearance_tuning.py— a--spacingCLI round-trip.test_cli.py—--spacinglisted in--help.test_plot_config.pyneeded no change; it already enforces thesignature/default/docstring contract.
Verification
scripts/check.sh(pytest, ruff, black) andscripts/build_docs.sh(mkdocs build --strict) both pass.Generated the docs assets on
mainand on this branch and compared byte-for-byte: of theeight images, only
h3n2_combined_label_connect.svgdiffers — the one example that usesconnect_leader_to_label=True. The other seven are identical, as expected.Re-rendered the chart that surfaced this, with
spacingat its default: the labels now sitclearly inside the frame line, confirming the inset alone resolves it without needing
spacingat all.Per
CLAUDE.md, this is a rendering-affecting change and there are deliberately noimage-snapshot tests — a human should still eyeball
site/examples.html.🤖 Generated with Claude Code