DEV Community

Muhammad Sheraz
Muhammad Sheraz

Posted on

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

chart tempelate 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
Enter fullscreen mode Exit fullscreen mode

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 is a great showcase of 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.

<script src="https://cdn.jsdelivr.net/npm/fscss@1.1.24/exec.min.js" async></script>

<style>
@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)
</style>

<div class="chart">
  <div class="chart-line line-1"></div>
  <div class="chart-line line-2"></div>
  <div class="chart-line line-3"></div>
  <div class="chart-grid"></div>
  <div class="y-axis">
    <span>0</span><span>20</span><span>40</span>
    <span>60</span><span>80</span><span>100</span>
  </div>
</div>

<div class="x-axis">
  <span>Sun</span><span>Mon</span><span>Tue</span>
  <span>Wed</span><span>Thu</span><span>Fri</span><span>Sat</span>
</div>
Enter fullscreen mode Exit fullscreen mode

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%;
`;
Enter fullscreen mode Exit fullscreen mode

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: fscss-ttr.github.io/st-core.fscss/multi-chart

Source: github.com/fscss-ttr/st-core.fscss


It's a refreshingly minimal take on data visualization — no installs, no configuration overhead, just CSS doing what it was always capable of.

Top comments (2)

Collapse
 
bhavin-allinonetools profile image
Bhavin Sheth

This is honestly refreshing — I’ve shipped dashboards where Chart.js felt like overkill for simple visuals. Using pure CSS like this keeps things lightweight and easier to control. Curious how it holds up with real-time updates at scale though 👀

Collapse
 
sheraz046 profile image
Muhammad Sheraz

thats reality