Skip to content

Charts

Three deterministic, pure-SVG charts — no chart library, no canvas. Color comes only from currentColor and the --ch-viz-* scale, every chart shares the loading / empty / ready state machine, and the crosshair is a prop, never a hover.

Sparkline

The inline trend line for asset rows — 24 hourly points, gradient fill under the line, sized to sit beside an AmountDisplay.

loading
<Sparkline data={[]} state="loading" />
  • --ch-bg-raised

Skeleton at exact chart size — no layout shift when data lands.

empty
No data
<Sparkline data={[]} state="empty" />
  • --ch-border-faint
  • --ch-fg-faint

No history yet — dashed bounds with the mono NO DATA mark.

ready
<Sparkline data={btc.sparkline} label="BTC 24-hour trend" />
  • --ch-viz-1

BTC, last 24 hours, on viz-1 with the gradient fill.

ready · line only
<Sparkline data={dbl.sparkline} fill={false} className="text-viz-4" />
  • --ch-viz-4

DBL on viz-4, fill off — for dense tables where stacked gradients muddy.

PriceChart

The full line + area chart with y-grid and x-ticks — here the BTC → XMR rate over 24 hours at 30-minute resolution. The crosshair prop pins a static marker with its value; there is no hover dependency to lose on touch.

loading
<PriceChart data={[]} state="loading" />
  • --ch-bg-raised

Full-size skeleton block.

empty
No data
<PriceChart data={[]} state="empty" />
  • --ch-border-faint
  • --ch-fg-faint

Pair has no rate history on this node yet.

ready
355.1349.7344.2338.814:3020:3002:3008:0014:00
<PriceChart data={rates} xLabels={["14:30","20:30","02:30","08:00","14:00"]} formatY={(v) => v.toFixed(1)} />
  • --ch-viz-1
  • --ch-viz-grid
  • --ch-fg-faint

48 points, four y-grid lines, five time ticks.

ready · crosshair
355.1349.7344.2338.814:3020:3002:3008:0014:00339.9
<PriceChart data={rates} xLabels={labels} crosshair={36} />
  • --ch-viz-1
  • --ch-border-strong
  • --ch-bg

Marker pinned at point 36 — dashed rule, ring dot, and the value read out above the plot.

AllocationDonut

Portfolio share as ring segments cycling viz-1 through viz-5 with hairline gaps, and a free center slot — here the fiat total.

loading
<AllocationDonut data={[]} state="loading" />
  • --ch-bg-raised
  • --ch-radius-pill

Circle skeleton at ring size.

empty
No data
<AllocationDonut data={[]} state="empty" />
  • --ch-border-faint
  • --ch-fg-faint

Fresh install — nothing funded yet.

ready
<AllocationDonut data={allocation}>
  <span>{totalFiat}</span>
</AllocationDonut>
  • --ch-viz-1
  • --ch-viz-2
  • --ch-viz-3
  • --ch-viz-4
  • --ch-viz-5
  • --ch-viz-grid

Six assets by fiat value, total in the center slot.

ready · single asset
<AllocationDonut data={[{ label: "BTC", value: 164741 }]} />
  • --ch-viz-1
  • --ch-viz-grid

One holding — a full ring with no segment gap.

Across themes

The allocation ring under dark, light, and Harbor Mode — the viz scale is tuned per layer, so series stay distinguishable on every background.

<AllocationDonut data={allocation} size={140}>
  <span>{totalFiat}</span>
</AllocationDonut>
  • --ch-viz-1
  • --ch-viz-2
  • --ch-viz-3
  • --ch-viz-4
  • --ch-viz-5