DEV Community

Cover image for I Built a Live Trading Dashboard With Zero Chart.js — Just CSS Custom Properties
FSCSS for FSCSS tutorial

Posted on • Originally published at i.devtem.org

I Built a Live Trading Dashboard With Zero Chart.js — Just CSS Custom Properties

Every time I needed a chart on a dashboard, the playbook was the same: pull in Chart.js or Recharts, fight with canvas sizing, write a wrapper component, and ship 40-80kb of JS just to draw a line. So with st-core.fscss — a library for FSCSS, CSS preprocessor — I went the other way: charts that are pure clip-path: polygon() shapes, animated with native CSS transitions, and driven entirely by CSS custom properties.

To prove it holds up outside a toy demo, I built Vela, a full portfolio/trading dashboard UI — KPI cards, a live performance chart with a benchmark overlay, allocation bars, sparklines, the works. You can poke at it live here:

🔗 Live preview: https://i.devtem.org/preview?id=5b5e4957-765b-4969-b1a0-3a4d0de495b4

No build step, no bundler — just index.html, dashboard.fscss, and main.js.

The core idea

A line chart is just a shape. If you have 8 data points on a 0-100 scale, you can describe the filled area under that line as a clip-path: polygon() with 8+2 coordinate pairs. st-core generates that polygon for you from CSS custom properties:

@st-chart-fill(.chart-fill)
@st-chart-line(.chart-line)
@st-chart-dots(.chart-dot-, 9px)
@st-chart-grid(.chart-grid, 6, 8)

.chart {
  @st-chart-points(28, 42, 35, 55, 48, 72, 60, 78)
}
Enter fullscreen mode Exit fullscreen mode

Those four @st-chart-* directives wire up a filled area, a stroked line, positioned dot markers, and a background grid — all from the same underlying point data. Swap the numbers in @st-chart-points, or update the matching --st-p1 through --st-p8 custom properties at runtime, and the shape redraws.

JS never touches a canvas

This is the part that actually matters for integration. main.js in Vela has zero charting logic — it only ever writes custom properties:

function setChartData(el, vals) {
  vals.forEach((v, i) => {
    el.style.setProperty(`--st-p${i + 1}`, `${100 - v}%`);
  });
}
Enter fullscreen mode Exit fullscreen mode

Because the chart shapes are driven by transition: clip-path 700ms cubic-bezier(.4,0,.2,1), animating between datasets is just... setting new values. There's a small easing loop in animateTo() for smoother interpolation, but even that is optional — CSS will tween clip-path changes on its own.

animateTo(mainChart, oldPoints, newPoints, 700);
Enter fullscreen mode Exit fullscreen mode

Swap a REST call in and you have live data:

const res = await fetch('/api/portfolio/history?period=1w');
const { points } = await res.json();
setChartData(document.getElementById('chart-line-portfolio'), points);
Enter fullscreen mode Exit fullscreen mode

Allocation bars and stat cards work the same way

st-core ships a few other primitives that follow the same pattern — a directive that hooks a custom property to a visual property:

@st-cat-bar-fill(.bar-fill)
@st-stat-card(.st-stat-card)
Enter fullscreen mode Exit fullscreen mode
function setBar(id, pct) {
  document.getElementById(id).style.setProperty('--st-cat-bar-fill-range', `${pct}%`);
}
Enter fullscreen mode Exit fullscreen mode

One CSS variable in, one bar width out. No JS animation library needed because the width transition lives in the stylesheet.

Why this matters

A few things fall out of this approach almost for free:

  • No JS dependency for rendering. The charts render even before main.js runs — they're just CSS shapes with default values baked into the stylesheet.
  • Themeable by design. Every color in Vela is a custom property (--st-accent, --st-green, --st-red...). Retheme the whole dashboard by overriding tokens in :root.
  • Animations are declarative. Hover tooltips, dot reveals, fade-ins — all transition and @keyframes, not requestAnimationFrame spaghetti (except where I wanted extra easing control).
  • Tiny footprint. No charting library payload. st-core compiles in the browser via the FSCSS runtime, or precompiles to plain CSS if you don't want runtime compilation at all.

Try it

The live preview link above is the full zip running as-is — sidebar nav, period switching (1D/1W/1M/3M/1Y), live-ticking BTC/ETH sparklines, the lot. Click around the period tabs and watch the chart redraw with the same clip-path mechanism described above.

Github repository: https://github.com/fscss-ttr/st-core.fscss

st-core.fscss is an open source MIT Licensed if you want to dig into the source, and Vela itself is listed as a template over on DevTemple if you'd rather skip straight to wiring up your own data.

Questions, pushback, "this is insane, why not just use a library"

Top comments (1)

Collapse
 
frank_signorini profile image
Frank

This is brilliant! I'm curious about