DEV Community

Cover image for Pure CSS Spider / Radar Charts Are Here in st-core.fscss v2
FSCSS tutorial for FSCSS tutorial

Posted on

Pure CSS Spider / Radar Charts Are Here in st-core.fscss v2

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

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);
}
Enter fullscreen mode Exit fullscreen mode
<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>
Enter fullscreen mode Exit fullscreen mode

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

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]);
Enter fullscreen mode Exit fullscreen mode

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

Enter fullscreen mode Exit fullscreen mode

Testing via CDN:

<script src="https://cdn.jsdelivr.net/npm/fscss@1.2.5/runtime.min.js" async></script>
Enter fullscreen mode Exit fullscreen mode
@import((*) from st-core@v2)
Enter fullscreen mode Exit fullscreen mode

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.

Top comments (1)

Collapse
 
suppdevbot profile image
DEV SUPPORTS •
You need to verify your account.
Enter fullscreen mode Exit fullscreen mode

tr.ee/dev-to