--- url: https://chartbuddy.io/embed/docs/axes/value-axis.md --- > Docs index: [llms.txt](https://chartbuddy.io/embed/docs/llms.txt) # Value axis The **primary range** (and secondary range) axes are linear value scales. On a vertical chart that is usually `axes.left`; on a horizontal chart it is `axes.bottom`. Always write to the **side that holds `id: 'primaryRange'`** (or `'secondaryRange'`) after orientation remap – see [Orientation](https://chartbuddy.io/embed/docs/concepts/orientation.md). ```js axes: { left: { id: 'primaryRange', labels: { preferredTickInterval: null, // auto customMin: null, customMax: null, numberFormat: { mode: 'custom', customFormat: { prefix: '', postfix: '', thousandSeparator: ',', decimalSeparator: '.', decimalPlaces: 0, forceDecimals: true, signDisplay: 'auto', scaleDown: 0, }, }, }, }, } ``` Full format field reference: [Number formats](https://chartbuddy.io/embed/docs/configuration/number-formats.md). *** ## Two modes: interval vs bounds Ticks and bounds are **mutually exclusive drivers**. Change one; do not fight both at once. | | Interval / auto mode | Bounds mode | |---|---|---| | Trigger | Leave min/max `null`; optionally set `preferredTickInterval` | Set `customMin` and/or `customMax` | | Bounds | Auto-round to multiples of the chosen interval | Fixed to your min/max | | Interval | Prefers your `preferredTickInterval` when allowed | Auto-picked so ticks land on the bounds | If you pin bounds **and** a preferred interval that cannot divide the span, the engine adjusts the interval (or ignores the preference). Chasing both by hand causes a feedback loop – pick one mode. *** ## Preferred interval `preferredTickInterval` is a **step size** (e.g. `10`, `25`, `50`), not “number of labels.” ```js axes: { left: { labels: { preferredTickInterval: 25, }, }, } ``` Leave it `null` for new charts so auto sparse selection runs from the space budget: | Space for (max labels) | Aim for | |---|---| | 1–5 | ~2 labels | | 6–13 | ~3 | | 14–20 | ~4 | | 21+ | ~5 | Nice candidates are values like 1, 2, 2.5, 5, 10 × powers of 10. **Do not hand-author `customTickInterval`.** The engine writes that field when resolving ticks. Authors set `preferredTickInterval` (or leave null). ### Percentage charts For `stackedBar100`, `stackedArea100`, and mekko value axes, allowed intervals are filtered to **divisors of 100** (1, 2, 2.5, 4, 5, 10, 20, 25, 50, 100). Prefer `20` / `25` / `50` for clean decks. Defaults already seed `postfix: '%'`. See [Percent axis sample](https://chartbuddy.io/embed/docs/samples/percent-axis.md). *** ## Bounds ```js axes: { left: { labels: { customMin: 0, customMax: 200, }, }, } ``` * `null` / omit – auto from data (+ nice rounding in interval mode) * Set one or both – bounds mode ### Zero rules | Chart family | Guidance | |---|---| | Clustered / stacked bar, stacked area, waterfall | Usually keep **0 in range**. Cutting off zero exaggerates differences. | | Line, scatter | Setting the range with `customMin` / `customMax` around the data is often right – zero is not required. | | 100% / mekko value | Pin **0–100** (engine and defaults already bias this way). | If `customMin` sits **above** the lowest data point (or `customMax` below the highest), bars/lines clip. Fix the bound or widen it. ### Recipe: zoomed line ```js { chartType: 'line', isDataTransposed: true, seriesData: [/* … */], axes: { left: { labels: { customMin: 80, customMax: 120, preferredTickInterval: 10, }, }, }, } ``` ### Recipe: currency ticks ```js axes: { left: { labels: { preferredTickInterval: 50, numberFormat: { mode: 'custom', customFormat: { prefix: '$', postfix: '', thousandSeparator: ',', decimalSeparator: '.', decimalPlaces: 0, forceDecimals: false, signDisplay: 'auto', scaleDown: 0, }, }, }, axisTitle: { visible: true, text: 'USD millions', }, }, } ``` Live: [Currency ticks sample](https://chartbuddy.io/embed/docs/samples/currency-ticks.md). *** ## Horizontal charts After `orientation: 'horizontal'`, the **primary range is on `bottom`**. Put formats, preferred intervals, bounds, and value gridlines there – not on `left` (that side is domain / categories). ```js { orientation: 'horizontal', axes: { bottom: { labels: { preferredTickInterval: 20, numberFormat: { mode: 'custom', customFormat: { prefix: '', postfix: '%', decimalPlaces: 0, forceDecimals: false, thousandSeparator: ',', decimalSeparator: '.', signDisplay: 'auto', scaleDown: 0, }, }, }, gridlines: { visible: true }, }, }, } ``` *** ## Gridlines Default for new charts: gridlines **`visible: false`**. Turn them on on the value axis side and pair overrides with a fixed `preferredTickInterval` so tick keys stay stable – [Gridlines](https://chartbuddy.io/embed/docs/configuration/gridlines.md). *** ## Pitfalls * Authoring `customTickInterval` instead of `preferredTickInterval` * Formatting `left` after flipping to horizontal (range moved to `bottom`) * Pinning bounds that clip data * Using dual custom min/max **and** an incompatible preferred interval * Expecting `chartData` number format alone to change PNG/slide look – formats are on the axis labels (and linked surfaces) Next: [Domain axis](https://chartbuddy.io/embed/docs/axes/domain.md) · [Secondary](https://chartbuddy.io/embed/docs/axes/secondary.md) · [By chart type](https://chartbuddy.io/embed/docs/axes/by-chart-type.md)