Skip to content

Reserve the axis gutter in a right-to-left container - #118

Open
3li7alaki wants to merge 2 commits into
TanStack:mainfrom
3li7alaki:rtl-axis-side-gutter
Open

Reserve the axis gutter in a right-to-left container#118
3li7alaki wants to merge 2 commits into
TanStack:mainfrom
3li7alaki:rtl-axis-side-gutter

Conversation

@3li7alaki

@3li7alaki 3li7alaki commented Aug 27, 2026

Copy link
Copy Markdown

Fixes #117.

The defect

A y scale with side: 'right' reserves no gutter when the container is
dir="rtl". The axis draws at the right edge, the plot keeps the full width
underneath it, and the tick labels are painted across the plot with the grid
lines running through them. Measured in the rendered SVGs, both 640px wide,
coordinates relative to each SVG's own left edge:

container plot x-range y tick labels rendered text-anchor
ltr 4 to 593 601 to 636 start
rtl, before 4 to 628 601 to 636 start
rtl, after 4 to 593 601 to 636 end

Why

createTickLabelCandidates derives the tick-label anchor from the physical
side alone:

guide.channel === 'y' ? (positiveSide ? 'start' : 'end') : ...

That anchor reaches the DOM as SVG text-anchor, which resolves against inline
base direction, so under dir="rtl" a right-side axis labelled start paints
leftward, into the plot rather than away from it.

Everything downstream is then faithful to that. The measurement mirrors with it,
as it should: the same string measures actualBoundingBoxLeft = -1.2, Right = 35.3 under ltr and 34.5 / -0.4 under rtl. includeBoundsMargin
sees a box on the plot side of the axis line, so margin.right stays at the
inset, chart.width grows, the axis slides further right, and the layout
converges on the overlap.

So the fix belongs at the anchor default rather than in the margin pass: the far
side takes end once the container reads right to left.

estimateSceneText needed the same mirror. It resolved anchor with a
left-to-right relation unconditionally, which happened to cancel the first bug
whenever no DOM measurer was present. Left alone, the anchor fix would have made
a host without one disagree with the browser instead.

Scope

Two source lines behind two internal parameters. No public API change, no new
type, no docs change. An author-supplied axis.ticks.anchor still lands
literally; only the default mirrors.

Untested and deliberately unchanged: an x axis picks its anchor from the rotate
sign, which is a different relation and may or may not need the same treatment.

Tests

Both fail on main and pass here.

  • cartesian-scales.test.ts asserts a right-side axis reserves the same gutter
    in both directions, through a measurer that mirrors the way the DOM one does.
    Without the fix the plot is 28px wider than its labels allow.
  • guide-layout.test.ts asserts the estimated painted box mirrors under rtl.

The scene-level test needs the mirroring measurer specifically: with the plain
estimator the two left-to-right assumptions cancel and nothing fails.

The second commit

The layout fix costs 73 B raw in shared code, which puts the difference-mark
increment 0.02 kB over its ceiling; gzip falls on most other entries. The second
commit relocks that one budget and refreshes the universal baseline. Drop it if
you would rather relock yourself.

Gates run locally: typecheck, test (1927 passing), docs:check,
package:check, bundle:check, and Prettier.

Summary by CodeRabbit

  • Bug Fixes

    • Fixed right-to-left chart layouts so right-side axis labels stay outside the plot area.
    • Improved RTL text alignment and spacing for axis and tick labels.
    • Ensured charts without a text measurer maintain consistent layout behavior.
  • Chores

    • Updated bundle size benchmarks and size budget records.

A y scale with `side: 'right'` took its tick-label anchor from the physical
side alone. That anchor reaches the DOM as SVG `text-anchor`, which resolves
against inline base direction, so an RTL container painted the labels leftward
into the plot. The margin pass then read those bounds correctly and reserved
nothing on the right, widening the plot until it ran under its own labels.

Anchor the far side with `end` once the container reads right to left, and
mirror the same relation in the text estimator so a host without a DOM
measurer resolves the identical layout.
The layout fix costs 73 B raw in shared code, which puts the difference-mark
increment 0.02 kB over its ceiling. Gzip falls on most entries.
@coderabbitai

coderabbitai Bot commented Aug 27, 2026

Copy link
Copy Markdown

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: ce697120-32ee-4866-be8a-abb62d97d1f2

📥 Commits

Reviewing files that changed from the base of the PR and between 258ed39 and 71ec035.

📒 Files selected for processing (7)
  • .changeset/rtl-axis-side-gutter.md
  • benchmarks/bundle-size/universal-baseline.json
  • packages/charts-core/src/cartesian-scales.test.ts
  • packages/charts-core/src/guide-layout.test.ts
  • packages/charts-core/src/guide-layout.ts
  • packages/charts-core/src/scene.ts
  • scripts/measure-bundles.mjs

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


📝 Walkthrough

Walkthrough

The change fixes right-side y-axis gutter reservation in RTL containers. Axis anchors and estimated text bounds now use RTL direction. Regression tests cover layout and text positioning. Bundle-size baselines and one gzip budget are updated.

Changes

RTL axis gutter correction

Layer / File(s) Summary
Propagate RTL direction through axis layout
packages/charts-core/src/scene.ts
Axis layout passes text direction through axis and tick-label creation. Default y-axis anchors now account for physical side and RTL direction.
Align text estimation and regression coverage
packages/charts-core/src/guide-layout.ts, packages/charts-core/src/guide-layout.test.ts, packages/charts-core/src/cartesian-scales.test.ts, .changeset/rtl-axis-side-gutter.md
RTL text estimation mirrors anchor origins. Tests verify text bounds and right-axis gutter reservation. A patch changeset records the fix.

Bundle size records

Layer / File(s) Summary
Update bundle size records
benchmarks/bundle-size/universal-baseline.json, scripts/measure-bundles.mjs
The bundle baseline uses new byte and gzip values. The incremental gzip budget increases from 7.03 to 7.06 KiB.

Estimated code review effort: 3 (Moderate) | ~20 minutes

Merge Risk: ⚪ Minimal · up to 71ec0

This localized change makes right-side RTL axis labels reserve the correct gutter and keeps fallback measurement aligned with browser rendering. No actionable merge-blocking risk remains.

Sequence Diagram(s)

sequenceDiagram
  participant compileSceneLayout
  participant createAxes
  participant createTickLabelCandidates
  participant estimateSceneText
  compileSceneLayout->>createAxes: pass RTL typography direction
  createAxes->>createTickLabelCandidates: pass rightToLeft
  createTickLabelCandidates->>estimateSceneText: create direction-aware label bounds
  estimateSceneText-->>compileSceneLayout: return mirrored text origin
Loading

Suggested reviewers: tannerlinsley

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ 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 10 functions across 5 files. (2 skipped: 2… Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ 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 describes the primary RTL axis gutter fix.
Linked Issues check ✅ Passed The changes fix issue #117 by mirroring default right-axis tick-label anchors under RTL, aligning estimated text bounds with DOM measurement, preserving explicit anchors, and adding regression tests.
Out of Scope Changes check ✅ Passed The changes remain within scope. The bundle baseline and budget updates support the shared-code size change described in the PR objectives.
Full details: Docstring Coverage

Explanation

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 10 functions across 5 files. (2 skipped: 2 unsupported.)

  • Fix all pre-merge checks with AI
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

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.

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.

A right-side y axis reserves no gutter under dir="rtl"

1 participant