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/fscss@1.1.24/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>
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;
}
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 -->
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}%`);
});
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.
Top comments (0)