kcolbchain  /  brand  /  components

charts

Plain SVG — no library, no canvas. A sparkline lives inside a card; a time series gets its own. Both use the same coordinate convention so the markup is identical except for height. Guide lines (peg, warn, critical) come from the token palette, not the chart library.

sparkline

Use inside a card to show the last N points of a single series. No axes, no legend — the surrounding card carries the label.

<svg class="spark" viewBox="0 0 100 48" preserveAspectRatio="none">
  <line class="peg"  x1="0" y1="24" x2="100" y2="24" />
  <line class="warn" x1="0" y1="14" x2="100" y2="14" />
  <line class="warn" x1="0" y1="34" x2="100" y2="34" />
  <line class="hot"  x1="0" y1="6"  x2="100" y2="6"  />
  <line class="hot"  x1="0" y1="42" x2="100" y2="42" />
  <path class="series s-usdc" d="M 0 24 L 10 23 L 20 25 …" />
</svg>

time series

The headline chart of a monitoring dashboard. Y-axis is bps deviation from peg, centred on zero so positive and negative drift read the same. Three series at most — one per coin. More than three and the eye loses them; switch to a small-multiples grid of sparklines instead.

peg deviation · last 60 ticks

USDC USDT DAI
+100 bps +50 bps peg -50 bps -100 bps
<div class="chart">
  <div class="chart-head">
    <h3>peg deviation · last 60 ticks</h3>
    <div class="legend">
      <span class="usdc"><span class="swatch"></span>USDC</span>
      <span class="usdt"><span class="swatch"></span>USDT</span>
      <span class="dai"><span class="swatch"></span>DAI</span>
    </div>
  </div>
  <svg class="ts" viewBox="0 0 800 220" preserveAspectRatio="none">
    <line class="peg"  x1="0" y1="110" x2="800" y2="110" />
    <line class="warn" x1="0" y1="80"  x2="800" y2="80"  />
    <line class="warn" x1="0" y1="140" x2="800" y2="140" />
    <line class="hot"  x1="0" y1="50"  x2="800" y2="50"  />
    <line class="hot"  x1="0" y1="170" x2="800" y2="170" />
    <path class="series s-usdc" d="..." />
    <path class="series s-usdt" d="..." />
    <path class="series s-dai"  d="..." />
  </svg>
</div>

plotting in plain JS

To draw a series from data, map each point's bps deviation to a Y coordinate using a fixed visual range. The fixed range keeps every chart comparable across coins and across reloads.

// y: 0 = top, H = bottom; centre peg at H/2.
// range = max bps to show on either side (e.g. 200 → ±200 bps fills the chart).
function ySVG(bps, range, H) {
  const clamped = Math.max(-range, Math.min(range, bps));
  return H / 2 - (clamped / range) * (H / 2 - 6); // 6px top/bottom padding
}

function pathFromSeries(points, peg, range, W, H) {
  const step = W / Math.max(1, points.length - 1);
  return points.map((p, i) => {
    const bps = ((p.price - peg) / peg) * 10000;
    const x = (i * step).toFixed(2);
    const y = ySVG(bps, range, H).toFixed(2);
    return (i === 0 ? 'M' : 'L') + ' ' + x + ' ' + y;
  }).join(' ');
}

palette

Series colors are desaturated versions of the token issuer palette — close enough that an operator recognises USDC/USDT/DAI, muted enough that no line out-shouts the others. The brand mark (yellow) is reserved for DAI when the chart needs a third hue; otherwise the third series falls back to var(--fg).

--s-usdc: #6aa3e0;   /* desaturated USDC blue */
--s-usdt: #6ec07a;   /* desaturated USDT green */
--s-dai:  var(--mark);  /* the brand yellow, used here as a data hue */

css

svg.spark { display: block; width: 100%; height: 48px; }
svg.ts    { display: block; width: 100%; height: 220px; }

svg .series { fill: none; stroke-width: 1.5; stroke-linejoin: round; }
svg .peg  { stroke: var(--line); stroke-dasharray: 3 3; }
svg .warn { stroke: var(--warn); stroke-dasharray: 2 4; opacity: 0.45; }
svg .hot  { stroke: var(--hot);  stroke-dasharray: 2 4; opacity: 0.45; }

svg .s-usdc { stroke: #6aa3e0; }
svg .s-usdt { stroke: #6ec07a; }
svg .s-dai  { stroke: var(--mark); }