Building Charts with Pure CSS — No SVG, No Canvas, No JS Required

Published: (April 16, 2026 at 12:29 AM EDT)
3 min read
Source: Dev.to

Source: Dev.to

chart template image

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-p1 through --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

MixinWhat it renders
@st-chart-lineLine path renderer
@st-chart-fillArea fill beneath the line
@st-chart-dotData point markers
@st-chart-gridBackground grid overlay
@st-chart-axis-x / @st-chart-axis-yAxis 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.

0 views
Back to Blog

Related posts

Read more »