Skip to content

Scales and axes

The scale

scale on the Chart sets the range of values it shows, as [min, max]. Every block in every Plot of the chart uses it.

<Chart scale={[0, 100]}>…</Chart>
<Chart scale={[-40, 40]}>…</Chart>      // negative values run the other way
<Chart scale={[0, Math.max(...values())]}>…</Chart> // a scale that follows the data

When the scale changes, everything on it moves together. A Bar that runs past the scale is cut at its end; dots and labels are drawn where their values are.

The axis

The Chart draws an axis on its own: grid lines with numbers.

  • ticks picks the lines: a list ([0, 25, 50, 75, 100]), a rough count (5), or false for none.
  • format turns a value into its text: (v) => v + "%".
import { Chart, Plot, Bar, Label } from "@bezda/rhp";

export default function Battery() {
  return (
    <Chart
      scale={[0, 100]}
      ticks={[0, 25, 50, 75, 100]}
      format={(v) => v + "%"}
    >
      <Plot device={["Phone", "Laptop", "Watch"]} charge={[64, 38, 91]}>
        {(d) => (
          <div>
            <Label edge="start">{d.device}</Label>
            <Bar to={d.charge} />
            <Label at={d.charge}>{d.charge}%</Label>
          </div>
        )}
      </Plot>
    </Chart>
  );
}

Drawing the scale yourself

For a scale that looks different (dashed lines, numbers in a pill, bands between ticks), put a Scale in the Chart. A Scale is a Plot of ticks: its row is drawn once per tick, and gets d.at, the tick’s value.

import { createSignal } from "solid-js";
import {
  Chart,
  Scale,
  Plot,
  Bar,
  Tick,
  Label,
  slat,
  every,
} from "@bezda/rhp";

// The scale is drawn by a slat too: one per tick, a dashed line and a
// number
// under the plot.
const Mark = slat(
  {
    room: { after: 22 },
    css: `
      .line {
        --rhp-tick-width: 1px;
        background: none;
        border-left: 1px dashed var(--rhp-grid);
      }
      .number {
        top: calc(100% + 6px);
        translate: -50% 0;
        --rhp-label-gap: 0px;
        color: var(--rhp-muted);
        font-size: 11px;
      }
    `,
  },
  (t) => (
    <div>
      <Tick at={t.at} thick={1} class="line" />
      <Label at={t.at} class="number">
        {t.at}
      </Label>
    </div>
  ),
);

export default function Rainfall() {
  const [max, setMax] = createSignal(60);
  return (
    <>
      <label class="demo-controls">
        Scale up to {max()} mm
        <input
          type="range"
          min="40"
          max="120"
          step="5"
          value={max()}
          onInput={(e) => setMax(+e.target.value)}
        />
      </label>
      <Chart scale={[0, max()]}>
        <Scale ticks={every(10)}>{Mark}</Scale>
        <Plot month={["May", "June", "July"]} rain={[38, 24, 11]}>
          {(d) => (
            <div>
              <Label edge="start">{d.month}</Label>
              <Bar to={d.rain} />
            </div>
          )}
        </Plot>
      </Chart>
    </>
  );
}

A Scale row also gets:

  • d.next, the next tick’s value, to fill the space between two ticks,
  • d.first and d.last, true at the two ends,
  • d.toEnd, how far the tick is from the end of the scale in pixels, to leave out a number that would crowd the end.

every(10) gives a tick at each multiple of 10. every(10, { ends: true }) adds the scale’s two ends as ticks.

A Chart with a Scale in it draws no axis of its own.