Skip to content

fresh-ui: no chart primitives — every consumer needing a time series hand-rolls one #1676

Description

@rickylabs

The gap

@netscript/fresh-ui ships no charting primitives. The documented surface is
Icon, DataGrid, Dropzone, the toast helpers, Accordion / Dialog /
Drawer / Popover / Sheet / Tabs / Tooltip, and the L0 primitives.

Any consumer that needs to show a metric over time therefore hand-rolls one. We
just did this for a second time in eis-chat (rickylabs/eis-chat#237, a usage &
cost surface), and ended up authoring three primitives from scratch:

Primitive What it does Why nothing existing covered it
AreaChart area/line over an ordered series, with a per-point hover readout no time-series primitive exists
ChartBlock stacked variant horizontal bars composed of separately-priced segments bar primitives were single-value
RadialChart concentric rings, one series per ring a donut partitions ONE whole; this compares series on independent axes

These are ~450 lines of SVG + CSS that are not app logic. They are a design
system's job, and every consumer is paying for them again.

Why a library isn't the answer

Not asking for a recharts dependency — the opposite. Deno + Preact islands, and
the dependency policy is Deno-native / @std → JSR → npm only as a stated
exception. A charting library is a large client bundle for what is, in the
common case, a few hundred lines of SVG driven by design tokens.

What's wanted is the same thing fresh-ui already does well for the other
primitives: token-driven, SSR-correct, theme-aware, no client JS unless the
interaction genuinely needs it.

What a useful primitive set looks like

From actually building these, the requirements that mattered:

  1. Tokens only, never a colour literal. These pages are theme-aware and a
    literal breaks exactly one theme, silently. Intents through data-tone
    against the semantic tokens (--ns-primary, --ns-success, …), same as the
    rest of fresh-ui.
  2. SSR-correct on first paint, no island required. The hover readout on our
    area chart is pure CSS (:hover on transparent hit-slots revealing a
    sibling). No hydration cost, no first-paint mismatch. Anything that needs
    client JS should be opt-in, not the baseline — we hit a real production bug
    where a chart region that depended on hydration painted and then vanished.
  3. Accessible without hover. Hover-only detail is invisible to keyboard and
    screen-reader users; the summary needs to be in aria-label / <title>.
  4. Formatting stays with the caller. Our charts take preformatted display
    strings. A chart primitive should not know about currencies or locales —
    ours renders CHF amounts converted at display time from stored USD, which is
    not something a chart could infer.
  5. Empty and partial states are first-class. This surface makes claims about
    money, so "no data" has to render as a labelled empty frame, never as a zero
    and never as a vanished panel. A primitive that returns null on an empty
    series pushes that decision onto every caller, and most will get it wrong.
  6. Stacked segments must stay separable. Our use case is cache-read vs
    cache-write tokens, which are priced differently — collapsing them into one
    bar makes the chart uncostable.

Suggested scope

A minimal @netscript/fresh-ui chart set that would have covered 100% of what
we needed:

  • <Chart variant="bar" | "column" | "stacked" | "line" | "area"> with
    data-tone intents, nice-axis ticks, and an optional CSS-only hover readout
  • <Donut> (single ring, share of a whole) and <Radial> (concentric, series
    on independent axes) — they answer different questions and shouldn't be one
    component
  • an EmptyChart frame so "no data" renders axes + an explanation rather than
    nothing

Happy to contribute ours upstream as a starting point if that's useful — they're
written to the @component / @layer / @depends theme-seed contract already.
Local implementations for reference: apps/dashboard/components/ui/area-chart.tsx,
radial-chart.tsx, and the stacked variant in chart-block.tsx in
rickylabs/eis-chat#237.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions