Default axis-padding to a 6px pixel gutter on every cartesian chart
Markers at data extremes no longer clip against axis frames. Scale range insets inward by 6px; axis lines stay at the frame edge. Opt out with `axisPadding: 0` or `false`.
Context
Markers at the data domain extremes clip against the axis frame today. A first-matchweek marker at x=1 sits exactly on the y-axis line; a top value at y=1.8 sits on the upper plot-area edge. Zero-config output is not publishable because the frame bisects markers.
Two distinct fixes exist in the wild:
- Domain padding (matplotlib
margins, ggplotexpand, HighchartsminPadding): widen the axis domain by a fraction of the range. Changes tick positions. Needs clamp semantics so padding doesn’t force an axis below 0 for count-like data. - Axis gutter / range inset (Observable Plot
inset, classic d3 pattern): leave the domain untouched; inset the scale range inside the frame by a few pixels. No clamp needed. No tick re-compute.
Decision
Adopt axis-gutter with axisPadding prop defaulting to 6 pixels (both
axes). Shape: number | [x, y] | false. Scale range insets by the gutter;
axis lines stay at the frame edge; data markers at domain extremes sit
inside the frame by exactly axisPadding px.
Per-chart overrides: XGTimeline defaults to [0, 6] (no x-gutter — the
timeline is bound by match-clock compression bands; shifting x-axis breaks
alignment). All other in-scope charts take the symmetric 6.
Domain-padding / clamp is deferred to a separate packet — most users only need the cosmetic fix, and layering domain-pad on top preserves zero-config simplicity.
Why 6
Six pixels = default marker diameter (radius 3 × 2). Smallest value that
guarantees marker-edge clearance for default styling. Observable Plot’s
default inset is also 6px. Highcharts’ default is minPadding: 0.05
(proportional) — translates to ~15px on a typical chart, which reads as
deliberately generous whitespace. We want tight editorial framing by
default, not Excel-style breathing room.
Consequences
- Every cartesian chart’s rendered output shifts by a few pixels. Golden SVG fixtures regenerated.
ChartCartesianAxesprimitive gained an optionalframeprop so the axis line can render at frame bounds while tick positions follow the (inset) plotArea. Default falls back toplotAreawhen noframe— backwards-compatible for any direct-primitive consumer.- Every affected model exposes
layout.framealongsidelayout.plotAreaso renderers can draw background + clipPath at outer bounds, ticks + markers at inset bounds. - Three charts (Beeswarm, DistributionComparison, SmallMultiples) have
non-standard layouts without a single
layout.plotArea. Deferred to a follow-up.