st-core.fscss is a pure-CSS charting system that renders line charts with no SVG, no Canvas, and no JavaScript charting library. It uses:
-
clip-path: polygon()for the line shapes - CSS custom properties (
--st-p1…--st-p8) for the 8 data points - FSCSS mixins that expand at compile/runtime into the required CSS
How Multi-Line Charts Work
You declare one @st-chart-line renderer. Every element that matches the class becomes an independent line.
- The chart container can hold a default dataset (
@st-chart-points). - Line 1 inherits that default.
- Additional lines override with their own
@st-chart-points. - Each line gets its own color via
backgroundorcolor.
X positions are fixed (0 %, 14 %, 28 %, 42 %, 57 %, 71 %, 85 %, 100 %).
Y values are given on a 0–100 scale (100 = top of the chart). The library inverts them internally for CSS coordinates.
Complete Multi-Line Chart Example
<script src="https://cdn.jsdelivr.net/npm/[email protected]/exec.min.js" async></script>
<style>
@import((*) from st-core)
@st-root()
.chart {
position: relative;
height: 200px;
background: var(--st-surface);
border-radius: var(--st-radius-lg);
overflow: hidden;
/* default dataset – inherited by line-1 */
@st-chart-points(20, 25, 21, 37, 30, 60, 27, 50)
}
/* single renderer for all lines */
@st-chart-line(.chart-line)
.chart-line {
@st-chart-line-width(2.5px)
}
/* line-1 inherits container data */
.line-1 {
background: #32D8D4;
}
/* line-2 has its own dataset */
.line-2 {
background: #E8A030;
@st-chart-points(10, 20, 16, 15, 66, 50, 80, 54)
}
/* line-3 has its own dataset */
.line-3 {
background: #B840C8;
@st-chart-points(5, 39, 20, 30, 27, 70, 60, 70)
}
/* optional grid + axes */
@st-chart-grid(.chart-grid, 10, 7)
@st-chart-axis-y(.y-axis)
.chart-grid {
opacity: 0.08;
}
</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>
Enter fullscreen mode Exit fullscreen mode
Key Rules
Rule Explanation One renderer@st-chart-line(.chart-line) is declared once
Default data
Container’s @st-chart-points is inherited by the first line
Overrides
Later lines set their own @st-chart-points
Color
Set with background (or color + currentColor)
Line thickness
@st-chart-line-width(2.5px) (default is 1.5 px)
Grid / axes
Optional helpers for visual polish
Design Tokens (@st-root)
Call @st-root() once (usually at the top of your stylesheet). It injects these CSS custom properties:
Colors
Token Default Purpose--st-bg
#0e0d14
Page background
--st-surface
#161422
Chart / surface background
--st-card
#1c1a2e
Card background
--st-accent
#9d7eff
Primary accent
--st-accent-2
#c4a8ff
Secondary accent
--st-accent-dim
#3a2e6e
Dimmed accent
--st-green
#4fffb0
Positive / up
--st-red
#ff5e7d
Negative / down
--st-text
#e8e3ff
Primary text
--st-muted
#6b6488
Muted text / axis labels
--st-border
rgba(157,126,255,.15)
Borders
Radius & Spacing
Token Default--st-radius-xl
40px
--st-radius-lg
16px
--st-radius-md
12px
--st-radius-sm
10px
--st-pad
24px
Chart-specific
Token Default Purpose--st-p1 … --st-p8
sample values
Y data points
--st-chart-line-width
1.5px
Stroke thickness
You can override any token globally or scoped to a single chart:
.chart {
--st-accent: #32D8D4;
}
Enter fullscreen mode Exit fullscreen mode
HTML Structure Summary
<div class="chart"> <!-- container + default data -->
<div class="chart-line line-1"></div> <!-- inherits default data -->
<div class="chart-line line-2"></div> <!-- own @st-chart-points -->
<div class="chart-line line-3"></div> <!-- own @st-chart-points -->
<div class="chart-grid"></div> <!-- optional -->
<div class="y-axis">…</div> <!-- optional -->
</div>
<div class="x-axis">…</div> <!-- optional, usually outside -->
Enter fullscreen mode Exit fullscreen mode
Runtime Data Updates (optional JS)
Because data lives in CSS variables, you can update any line by writing properties:
const line2 = document.querySelector('.line-2');
[15, 30, 25, 40, 55, 45, 70, 60].forEach((v, i) => {
line2.style.setProperty(`--st-p${i + 1}`, `${100 - v}%`);
});
Enter fullscreen mode Exit fullscreen mode
The browser repaints the clip-path automatically — no redraw library needed.
Sources
Repository and examples:
Help us star the repo if it helps 🙏
This gives you a zero-JS-runtime (or near-zero) multi-series line chart that stays extremely lightweight.