npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@fundar/data-chart-telling

v0.0.14

Published

A Svelte 5 component library for building data visualisation charts, built on top of [svelteplot](https://svelteplot.dev). It provides low-level **plot** primitives (single graphs) and will grow to include higher-level **chart** compositions with titles,

Readme

data-chart-telling

A Svelte 5 component library for building data visualisation charts, built on top of svelteplot. It provides low-level plot primitives (single graphs) and will grow to include higher-level chart compositions with titles, legends, facets, and timelines.


Build & publish

After making changes, rebuild the package distributable:

pnpm build

prepack runs svelte-package followed by publint automatically before packing, so a publish will always ship a clean build:

# dry-run — inspect what will be uploaded
pnpm pack

# publish to the npm registry
pnpm publish --access public

Bump version in package.json before each publish.


Tests

Unit tests (node)

Pure logic tests — no browser required.

pnpm test

This runs both the node unit project and the browser component project (Chromium via Playwright).

To run only the node unit tests:

pnpm vitest run --project=unit

To run only the browser component tests:

pnpm vitest run --project=browser

Storybook interaction tests

Run stories as Vitest tests (requires Storybook to boot internally):

pnpm test:stories

Storybook

Serve the component explorer locally:

pnpm storybook
# → http://localhost:6006

Build a static Storybook for deployment:

pnpm build-storybook

Chart types

Vocabulary: a plot is a single graph (axes + marks). A chart wraps a plot with a title, caption, legend, facets, and/or a timeline.

Charts

| Component | Import path | Description | | --- | --- | --- | | BarChart | $lib/charts/bar/Chart.svelte | Vertical bar chart with title/caption/legend/facet/timeline chrome. | | HeatmapChart | $lib/charts/heatmap/Chart.svelte | Grid of colour-coded cells. Colour domain is computed over the full dataset for cross-facet comparability. | | LineChart | $lib/charts/line/Chart.svelte | Line chart. Default timeline mode is upTo (cumulative reveal). | | PyramidChart | $lib/charts/pyramid/Chart.svelte | Population-pyramid-style diverging bar chart with a shared zero baseline. |

Plots

| Component | Import path | Description | | --- | --- | --- | | BarPlot | $lib/plots/bar/Plot.svelte | Vertical bar chart. Supports multiple series and per-series range segments. | | HeatmapPlot | $lib/plots/heatmap/Plot.svelte | Grid of colour-coded cells. | | LinePlot | $lib/plots/line/Plot.svelte | Line chart. Supports multiple series and per-series range segments with independent stroke styles. | | PyramidPlot | $lib/plots/pyramid/Plot.svelte | Diverging horizontal bar chart; each series is mirrored left or right of a shared zero baseline. |

All components accept explicit width and height in pixels (svelteplot does not auto-size).


Plot props

Plots accept either series (pre-built Series[]) or the data + x/y/z flat-rows path — these are mutually exclusive.

| Prop | Type | Default | BarPlot | HeatmapPlot | LinePlot | PyramidPlot | | ---- | ---- | ------- | ------- | ----------- | -------- | ----------- | | width | number | — | ✅ | ✅ | ✅ | ✅ | | height | number | — | ✅ | ✅ | ✅ | ✅ | | series | Series[] | — | ✅ | ✅ | ✅ | ✅ | | data | TData[] | — | ✅ | ✅ | ✅ | ✅ | | x | Accessor | — | ✅ | ✅ | ✅ | ✅ | | y | Accessor | — | ✅ | ✅ | ✅ | ✅ | | z | Accessor | — | ✅ | ✅ | ✅ | ✅ | | segments | Segments<TStyle> | [] | ✅ | ✅ | ✅ | ✅ | | markers | Marker[] | [] | ✅ | ✅ | ✅ | ✅ | | scales | ScalesConfig | {} | ✅ | ✅ | ✅ | ✅ | | margins | MarginConfig | {} | ✅ | ✅ | ✅ | ✅ | | tooltip | TooltipRuntime | | ✅ | ✅ | ✅ | ✅ | | styles | PlotStyles | | ✅ | ✅ | ✅ | ✅ |

PlotStyles sub-properties (plots)

| Sub-prop | Type | BarPlot default | HeatmapPlot default | LinePlot default | PyramidPlot default | | -------- | ---- | --------------- | ------------------- | ---------------- | ------------------- | | styles.values.show | boolean | false | true | false (last point) | false | | styles.values.format | (v: number, seriesName: string) => string | v => \${v}`|v => `${v}`|(v, name) => `${name} - ${v}`|v => `${v}`| |styles.values.anchor|'outside' | 'start' | 'middle' | 'end'|'outside'| — |'start'|'outside'| |styles.values.dx|number| | | | | |styles.values.dy|number| | | | | |styles.values.textAnchor|'start' | 'end' | 'middle'| |'middle'| | | |styles.values.lineAnchor|'top' | 'bottom' | 'middle'| |'middle'| | | |styles.values.rotate|number| | | | | |styles.values.class|string| | | | | |styles.values.textClass|string| | | | | |styles.colors.min|string| — | cfgcontinuous.min| — | — | |styles.colors.max|string| — | cfgcontinuous.max| — | — | |styles.colors.left|string| — | — | — | palette index 0 | |styles.colors.right|string` | — | — | — | palette index 1 |


Chart props

| Prop | Type | Default | BarChart | HeatmapChart | LineChart | PyramidChart | | ---- | ---- | ------- | -------- | ------------ | --------- | ------------ | | width | number | — | ✅ | ✅ | ✅ | ✅ | | height | number | — | ✅ | ✅ | ✅ | ✅ | | data | TRow[] | — | ✅ | ✅ | ✅ | ✅ | | x | Accessor | — | ✅ | ✅ | ✅ | ✅ | | y | Accessor | — | ✅ | ✅ | ✅ | ✅ | | z | Accessor | — | ✅ | ✅ | ✅ | ✅ | | title | string | | ✅ | ✅ | ✅ | ✅ | | subtitle | string | | ✅ | ✅ | ✅ | ✅ | | caption | string | | ✅ | ✅ | ✅ | ✅ | | header | Snippet | | ✅ | ✅ | ✅ | ✅ | | footer | Snippet | | ✅ | ✅ | ✅ | ✅ | | legend | LegendSection[] | | ✅ | ✅ | ✅ | ✅ | | segments | Segments<TStyle> | [] | ✅ | ✅ | ✅ | ✅ | | markers | Marker[] | [] | ✅ | ✅ | ✅ | ✅ | | scales | ScalesConfig | {} | ✅ | ✅ | ✅ | ✅ | | margins | MarginConfig | {} | ✅ | ✅ | ✅ | ✅ | | facet | FacetConfig | | ✅ | ✅ | ✅ | ✅ | | timeline | TimelineConfig | | ✅ | ✅ | ✅ | ✅ | | tooltip | TooltipProp | | ✅ | ✅ | ✅ | ✅ | | styles | PlotStyles | | ✅ | ✅ | ✅ | ✅ |

header and footer are Svelte 5 snippets that replace the string props when richer markup is needed (custom styles, logos, links, etc.). When header is provided it takes full precedence over title/subtitle; when footer is provided it takes full precedence over caption. Use the natural snippet syntax:

<LineChart ...>
  {#snippet header()}
    <div style="display:flex; justify-content:space-between">
      <h3 style="color:#2563eb">My Chart</h3>
      <img src="/logo.svg" alt="Logo" />
    </div>
  {/snippet}

  {#snippet footer()}
    <small>Source: <a href="...">Dataset</a> · Updated Q3 2024</small>
  {/snippet}
</LineChart>

See the PlotStyles sub-properties table above — charts accept the same styles shape and pass it through to their underlying plot.


Segments

The segments prop separates data ranges from visual styling. Every plot kind shares the same underlying types; the style shape (TStyle) differs per mark.

| Plot | segments type | | ---- | --------------- | | LinePlot / LineChart | Segments<LineSegmentStyle> | | BarPlot / BarChart | Segments<BarSegmentStyle> | | PyramidPlot / PyramidChart | Segments<BarSegmentStyle> | | HeatmapPlot / HeatmapChart | Segments<CellSegmentStyle> ('default' key only) |

Core types

type Range           = { from?: AxisValue; to?: AxisValue };  // inclusive on both sides
type Area            = { x?: Range; y?: Range };              // 2-D rectangular region
type Segment<TStyle> = { areas?: Area[]; style?: TStyle };

A Segment matches a data point (or heatmap cell) when the point falls inside any of its areas. Omitting areas creates a catch-all that matches every data point — but catch-alls are always evaluated last, regardless of their position in the list, so area-specific segments always win. This lets a catch-all placed first act as a fallback baseline that highlighted ranges override.

An Area restricts by x, y, or both. Both bounds of a Range are inclusive. Omitting x or y leaves that axis unrestricted.

Segments structure

segments is always an array of groups. Each group is a plain object whose keys map to Segment[] lists:

// 'default' key — applies its segments to every series
type DefaultSegmentsMap<TStyle>     = { default: Segment<TStyle>[] };

// named keys — applies segments only to the named series
type SeriesAwareSegmentsMap<TStyle> = Record<string, Segment<TStyle>[]>;

// one element of the array — either form, or a mix of both key types
type SegmentsStyle<TStyle> = DefaultSegmentsMap<TStyle> | SeriesAwareSegmentsMap<TStyle>;

// the prop type: an ordered list of groups
type Segments<TStyle> = SegmentsStyle<TStyle>[];

Having multiple groups lets you organise segments by concern — for example one group for per-series stroke colours and a second for shared dot markers applied via 'default'. Groups are processed in order and their resolved segments are concatenated per series before evaluation.

Examples

Line chart — baseline colour with a highlighted range (catch-all as fallback):

<LinePlot
  {width} {height}
  series={[{ name: 'gdp', x: 'year', y: 'value', data: GDP }]}
  segments={[
    {
      gdp: [
        { style: { stroke: { stroke: '#94a3b8', strokeWidth: 1 } } },
        { areas: [{ x: { from: 2008, to: 2010 } }],
          style: { stroke: { stroke: '#ef4444', strokeWidth: 3 } } },
      ],
    },
  ]}
/>

Line chart — per-series baseline colours + shared dot markers (two-group approach):

<LinePlot
  {width} {height}
  data={LIFE_EXPECTANCY} x="year" y="value" z="sex"
  segments={[
    // Group 1: per-series catch-all stroke colours
    { female: [{ style: { stroke: { stroke: 'steelblue' } } }],
      male:   [{ style: { stroke: { stroke: 'tomato'    } } }] },
    // Group 2: dots for all series in a specific range
    { default: [
        { areas: [{ x: { from: 2010, to: 2012 } }],
          style: { dots: { dotRadius: 5, dotSymbol: 'circle' } } },
      ],
    },
  ]}
/>

Heatmap — highlight specific columns, rows, and individual cells:

<HeatmapChart
  {width} {height} {data} {x} {y} {z}
  segments={[
    {
      default: [
        { areas: [{ x: { from: 'Q1', to: 'Q1' } }, { x: { from: 'Q3', to: 'Q3' } }],
          style: { stroke: '#f59e0b', strokeWidth: 2 } },
        { areas: [{ x: { from: 'Q2', to: 'Q2' }, y: { from: 1, to: 2 } }],
          style: { fill: '#ef4444', fillOpacity: 0.8 } },
      ],
    },
  ]}
/>

Omitting segments (or passing []) renders every mark in its default palette colour.


Public API

import {
  ...
} from '@fundar/data-chart-telling';