Building Charts with Pure CSS — No SVG, No Canvas, No JS Required
Source: Dev.to

Most developers reach for a chart library the moment they need a visualization — Chart.js, Recharts, D3 — and suddenly their page is carrying a hefty bundle just to draw a few lines. What if CSS alone could handle it?
That’s exactly what st‑core.fscss pulls off. It renders fully functional line charts using nothing but browser‑native CSS features, compiled at build time.
The Mechanism Behind It
Three CSS primitives do all the heavy lifting:
clip-path: polygon()– shapes each line visually- CSS custom properties (
--st-p1through--st-p8) – hold the data points - FSCSS mixins – generate the chart structure during compilation
The data pipeline looks like this:
data → CSS variables → clip-path → rendered chart
Everything is resolved before the browser even touches it. No runtime rendering, no extra DOM depth, no JavaScript dependency for visuals.
Rendering Multiple Lines
The multi‑line chart showcases how elegantly this scales. The approach uses one shared renderer with per‑element data overrides — each line element carries its own dataset via scoped CSS variables.
@import((*) from st-core);
@st-root();
.chart {
height: 200px;
position: relative;
@st-chart-points(20, 25, 21, 37, 30, 60, 27, 50);
}
@st-chart-line(.chart-line);
.chart-line {
background: currentColor;
@st-chart-line-width(2px);
}
.line-1 { color: #32D8D4; }
.line-2 {
color: #E8A030;
@st-chart-points(10, 20, 16, 15, 66, 50, 80, 54);
}
.line-3 {
color: #B840C8;
@st-chart-points(5, 39, 20, 30, 27, 70, 60, 70);
}
@st-chart-grid(.chart-grid, 10, 7);
@st-chart-axis-y(.y-axis);
@st-chart-axis-x(.x-axis);
02040
6080100
SunMonTue
WedThuFriSat
Each .line-* element overrides the default dataset from .chart. The renderer picks up whichever --st-p* variables are in scope for that element. Clean, composable, predictable.
Available Mixins at a Glance
| Mixin | What it renders |
|---|---|
@st-chart-line | Line path renderer |
@st-chart-fill | Area fill beneath the line |
@st-chart-dot | Data point markers |
@st-chart-grid | Background grid overlay |
@st-chart-axis-x / @st-chart-axis-y | Axis label layouts |
All of these compile down to plain CSS — nothing ships to the browser that wasn’t already resolved.
Handling Dynamic Data
For use cases where data changes at runtime, you can push updated values directly from JavaScript:
chart.style.cssText = `
--st-p1: 40%;
--st-p2: 75%;
--st-p3: 60%;
`;
JavaScript passes the values; CSS renders the result. Transitions work as expected if you’ve defined them — no additional wiring needed.
Why This Approach Stands Out
- Zero runtime overhead – charts are compiled into static CSS, not computed on each render
- No third‑party bundle – the browser’s rendering engine does the visual work natively
- Plain custom properties – data lives in CSS, not buried inside a config object or framework component
- Full stylistic control – nothing is locked into a preset theme or opinionated design system
Try It
Live demo:
Source:
It’s a refreshingly minimal take on data visualization — no installs, no configuration overhead, just CSS doing what it was always capable of.