No SVG. No canvas. No JS charting library.
st-core.fscss v2 now draws spider (radar) charts the same way it draws area and line charts: FSCSS arrays, CSS custom properties, and clip-path: polygon(). The result looks like a modern product dashboard — skills matrices, multi-axis scores, team comparisons — and the payload stays tiny.
The module Source:
github.com/fscss-ttr/st-core.fscss
Why this feels modern
Modern UI is not “more libraries.” It’s clarity, density, and motion that doesn’t cost a framework.
Spider charts in st-core lean on:
- Token-driven color —
--st-accent,--st-surface,--st-border. Change one variable; the whole chart follows. - Soft polar geometry — fill via
color-mix, stroke as a dual-pass polygon, rings and spokes as gradients. No harsh SVG strokes fighting your theme. - Composable layers — grid, spokes, fill, line, dots stack as plain divs. Multi-series is just more layers with their own accent.
- Native inheritance — coordinates are custom properties. Children inherit. Multi-team cards don’t need a state tree; they need scoped hosts.
That combination reads as “product UI,” not “demo from 2014.”
Why st-core stays lightweight
| What you don’t ship | What you do ship |
|---|---|
| Chart.js / D3 / Recharts | A few @define mixins |
| SVG path builders | clip-path: polygon() |
| Canvas + DPR logic | CSS % + sin() / cos() in calc() |
| Hydration for the chart | Optional JS that only sets variables |
Geometry is decided at compile time from @arr length. Runtime work is rewriting --st-sxN / --st-syN (or --st-pN for linear charts). Animations can use transition: clip-path on the compositor.
Compiled CSS for a chart class stays on the order of about a kilobyte, not a hundred. The page paints charts with zero chart-library JavaScript.
That’s the st-core bet: visualization as styling, not as an app runtime.
The one rule (same as linear charts)
Points write. Renderers read.
@st-spider-points(array) → writes --st-sxN / --st-syN on the host @st-spider-fill / line / dots → only read those variables Call points on the host that owns the series. Multi-series means one host per series — same pattern as multi-line area charts in v2.
Angle 0° is at the top. Values are 0–100 scores scaled by rmax (default 42% of the box).
What you can build
1. Minimal radar
Six axes, one series, grid + spokes + fill + line + dots.
@import((*) from st-core@v2) @st-root() @st-spider-root() @arr skills[80, 65, 90, 55, 70, 85] @st-spider-fill(.spider-fill, skills) @st-spider-line(.spider-line, skills) @st-spider-dots(.spider-dot-, skills, 9px) @st-spider-grid(.spider-grid) @st-spider-spokes(.spider-spokes, skills) .spider { @st-spider-points(skills, 42) position: relative; width: 280px; height: 280px; border-radius: 50%; background: var(--st-surface); } <div class="spider"> <div class="spider-grid"></div> <div class="spider-spokes"></div> <div class="spider-fill"></div> <div class="spider-line"></div> <div class="spider-dot-1"></div> <!-- … through .spider-dot-6 --> </div> Dots need real DOM nodes — the mixin only generates the CSS.
2. Team comparison cards
Two hosts, two accents, shared label layout:
@arr teamA[92, 78, 85, 55, 48, 80] @arr teamB[68, 82, 70, 95, 62, 58] .spider-wrap.team-a { @st-spider-points(teamA, 42) --st-accent: #9d7eff; --st-spider-fill-opacity: 28%; } .spider-wrap.team-b { @st-spider-points(teamB, 42) --st-accent: #4fffb0; --st-spider-fill-opacity: 30%; } Axis labels are ordinary absolute spans (Speed, Quality, UX, Scale, Cost, Support,...) around the circle. No special chart API — just layout.
3. Multi-series overlay
One polar grid, five polygons, each series a full-size absolute layer with its own @st-spider-points and --st-accent. Lower --st-spider-fill-opacity keeps overlaps readable.
4. Live updates without a library
function polarVars(values, rmax = 42) { const n = values.length; return values.map((v, i) => { const ang = (i * 360) / n; const r = (v / 100) * rmax; return ( `--st-sx${i + 1}: calc(50% + (${r} * 1%) * sin(${ang} * 1deg));` + `--st-sy${i + 1}: calc(50% - (${r} * 1%) * cos(${ang} * 1deg));` ); }).join(" "); } host.style.cssText = polarVars([80, 65, 90, 55, 70, 85]); Same length as the compiled polygon; values morph. Optional transition on clip-path for soft motion.
Under the hood (short)
| Layer | Technique |
|---|---|
| Coordinates | --st-sxN / --st-syN from sin / cos in calc() |
| Fill | Closed polygon() + color-mix with --st-spider-fill-opacity |
| Stroke | Dual-pass polygon; inner path scaled by --st-spider-stroke-scale |
| Rings | radial-gradient stops at 20 / 40 / 60 / 80% |
| Spokes | Thin conic-gradient bands from -90deg + center mask |
Everything is documented in the repo: README (API + examples), EXPLAINED (every @define), integration/html (dual-team and multi-series demos).
Install
NPM CLI:
npm install -g fscss charts.fscss charts.css Testing via CDN:
<script src="https://cdn.jsdelivr.net/npm/fscss@1.2.5/runtime.min.js" async></script> @import((*) from st-core@v2) Prefer FSCSS CLI for production CSS with zero runtime preprocessor.
Requires FSCSS v1.2.3+. MIT licensed.
Area and line charts proved that dashboards don’t need a JS chart runtime. Spider charts extend that idea into polar space without changing the contract: arrays in, custom properties out, polygons on screen.
Explore the module, the demos, and the under-the-hood write-up here:
github.com/fscss-ttr/st-core.fscss
Pure CSS. Still lightweight. Now with radar.

