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

@zvk/charts

v0.1.3

Published

Dependency-free chart utilities and server-safe SVG React components for ZVK dashboards.

Readme

@zvk/charts

Dependency-free chart utilities and server-safe SVG React components for ZVK dashboards.

import { createLinearScale, deriveBarChartModel, deriveSparklineModel, deriveSparklineSummary, linePath, planChartTicks } from "@zvk/charts";
import { AreaChart, BarChart, BulletChart, GroupedBarChart, Histogram, LineChart, ScatterPlot, Sparkline, StackedBarChart } from "@zvk/charts/react";
import "@zvk/charts/styles.css";

@zvk/charts is static-first. The root package exports pure chart math, layout, bar, sparkline, line-detail, axis, diagnostics, and table-model helpers. @zvk/charts/react exports deterministic React 19 components that render SVG figures without browser APIs at module initialization. @zvk/charts/styles.css provides the .zvk-chart* class and --zvk-chart-* variable contract.

The package has no runtime dependencies. React and React DOM are peers for the React subpath.

Chart Types

v1 includes:

  • BarChart for categorical comparison.
  • GroupedBarChart for comparing independent series inside each category.
  • StackedBarChart for additive part-to-whole bar segments.
  • LineChart for ordered or time-like trends.
  • AreaChart for filled trend volume.
  • Sparkline for compact KPI trends.
  • BulletChart for progress against target ranges.
  • ScatterPlot for relationships between two numeric measures.
  • Histogram for numeric distributions.
  • ChartFrame, ChartLegend, and ChartDataTable for static accessible chart composition.
  • Utilities for linear scales, band scales, nice numeric ticks, UTC millisecond normalization, bar layout, line paths, line detail models, bar models, grouped/stacked/area/bullet/scatter/histogram models, annotations, sparkline summaries, small-multiple layout planning, density planning, axis tick planning, reference marks, diagnostics, and table models.

Deferred by design:

  • rich tooltips;
  • animation;
  • pie/donut, stacked area, brush/zoom, and graph/node-link visualization;
  • canvas/WebGL renderers;
  • D3, Recharts, Chart.js, Nivo, ECharts, visx, or Tremor dependencies.

Accessibility

Charts are complex images. Every chart component requires:

  • title;
  • description;
  • readable series/category labels;
  • formatters for values whose raw numbers are not user-facing;
  • a visible summary or table fallback for decision-making charts.

Static React components render a <figure> and an SVG with role="img", visible title/description IDs, and SVG <title> / <desc> fallbacks. Use showDataTable when exact values matter.

type RevenueRow = {
  month: "Jan" | "Feb" | "Mar";
  revenue: number;
};

const data = [
  { month: "Jan", revenue: 12000 },
  { month: "Feb", revenue: 14200 },
  { month: "Mar", revenue: 13800 }
] satisfies RevenueRow[];

<BarChart
  data={data}
  description="Revenue peaked in February and stayed above January in March."
  showDataTable
  title="Monthly revenue"
  valueFormatter={(value) => `$${value.toLocaleString("en-US")}`}
  x="month"
  y="revenue"
/>;

Grouped and stacked bars are separate exports so their multi-series semantics stay explicit:

<GroupedBarChart
  category="quarter"
  data={[
    { quarter: "Q1", enterprise: 124, selfServe: 66 },
    { quarter: "Q2", enterprise: 148, selfServe: 72 }
  ]}
  description="Enterprise and self-serve revenue compared by quarter."
  series={[
    { id: "enterprise", label: "Enterprise", value: "enterprise" },
    { id: "self-serve", label: "Self serve", value: "selfServe" }
  ]}
  showDataTable
  title="Segment revenue"
/>;

<StackedBarChart
  category="quarter"
  data={[
    { quarter: "Q1", new: 42, expansion: 18, churn: -9 },
    { quarter: "Q2", new: 48, expansion: 24, churn: -12 }
  ]}
  description="New and expansion revenue are stacked against churn."
  series={[
    { id: "new", label: "New", value: "new" },
    { id: "expansion", label: "Expansion", value: "expansion" },
    { id: "churn", label: "Churn", tone: "negative", value: "churn" }
  ]}
  showDataTable
  title="ARR movement"
/>;

Area, bullet, scatter, and histogram components cover common static dashboard foundations:

<AreaChart
  data={data}
  description="Revenue filled to the baseline for the quarter."
  series={[{ id: "revenue", label: "Revenue", x: "month", y: "revenue" }]}
  showDataTable
  title="Revenue area"
  xScale="linear"
/>;

<BulletChart
  data={[{ label: "Revenue", value: 72, target: 80, warning: 60, good: 90 }]}
  description="Progress against target and qualitative ranges."
  label="label"
  ranges={[
    { id: "warning", label: "Warning", value: "warning", tone: "warning" },
    { id: "good", label: "Good", value: "good", tone: "positive" }
  ]}
  target="target"
  title="KPI health"
  value="value"
/>;

LineChart can render static point markers and latest/extrema value labels without adding client JavaScript:

<LineChart
  data={data}
  description="Revenue peaked in February and stayed above January in March."
  pointLabels="latest"
  series={[{ id: "revenue", label: "Revenue", x: "month", y: "revenue", unit: "USD" }]}
  showPoints
  title="Revenue trend"
  xScale="linear"
  valueFormatter={(value) => `$${value.toLocaleString("en-US")}`}
/>;

Line charts can also render static x-axis labels and labeled reference marks:

<LineChart
  data={data}
  description="Revenue stayed near the target range through the quarter."
  referenceBands={[{ axis: "y", from: 12000, id: "target-range", label: "Target range", to: 15000 }]}
  referenceLines={[{ axis: "y", id: "goal", label: "Goal", value: 14000, tone: "warning" }]}
  series={[{ id: "revenue", label: "Revenue", x: "month", y: "revenue", unit: "USD" }]}
  title="Revenue trend"
  xAxis={{ showLabels: true, tickFormatter: (value) => String(value) }}
  xScale="linear"
/>;

Use annotations for static callouts anchored to points or bars:

<LineChart
  annotations={[{ anchor: { kind: "point", seriesId: "revenue", datumIndex: 2 }, id: "latest", label: "Latest" }]}
  data={data}
  description="Revenue increased through the quarter."
  series={[{ id: "revenue", label: "Revenue", x: "month", y: "revenue" }]}
  title="Revenue trend"
  xScale="linear"
/>;

Use deterministic axis options when dashboard copy needs stable labels. tickCount is a hint, tickValues is exact, and tickFormatter should be SSR-stable. Dense labels and duplicate formatted labels are reported through diagnostics instead of browser measurement.

For app-owned details panels, derive the same server-safe models used by the React charts:

import { createLinearScale, deriveBarChartModel, deriveLineChartDetailModel, deriveSparklineModel, deriveSparklineSummary, planChartTicks } from "@zvk/charts";

const lineModel = deriveLineChartDetailModel({
  data,
  plotHeight: 240,
  plotWidth: 480,
  series: [{ id: "revenue", label: "Revenue", x: "month", y: "revenue", unit: "USD" }],
  xScale: "linear"
});

const barModel = deriveBarChartModel({
  data,
  height: 260,
  title: "Monthly revenue",
  width: 480,
  x: "month",
  y: "revenue"
});

const sparklineModel = deriveSparklineModel({
  data,
  value: "revenue",
  valueFormatter: (value) => `$${value.toLocaleString("en-US")}`
});

const sparklineSummary = deriveSparklineSummary({
  includePercentDelta: true,
  values: data.map((datum) => datum.revenue),
  valueFormatter: (value) => `$${value.toLocaleString("en-US")}`
});

const tickPlan = planChartTicks({
  axis: "y",
  maxLabelCount: 4,
  scale: createLinearScale({ domain: [0, 20000], range: [240, 0] }),
  tickValues: [0, 5000, 10000, 15000, 20000]
});

deriveBarChartModel exposes resolved categories, bars, value ticks, reference marks, table rows, and diagnostics before React renders. Grouped, stacked, area, bullet, scatter, and histogram models expose geometry and table metadata for persistent details panels. deriveSparklineModel exposes the path, latest value, min/max, delta, direction, and formatter-backed summary for compact KPI cards. deriveSparklineSummary creates formatter-driven KPI copy, including optional percent delta. planChartTicks, planVisiblePointLabels, and planSmallMultiples return visibility/layout state and diagnostics for omitted dense labels or repeated chart panels.

Pure helpers return diagnostics for invalid data, duplicate IDs, unresolved annotation anchors, omitted dense labels or markers, duplicate formatted tick labels, and out-of-domain reference marks. Static React components surface diagnostic count and highest severity as data-chart-diagnostic-count and data-chart-diagnostic-severity attributes instead of invoking callbacks during render.

import { deriveReferenceMarks } from "@zvk/charts";

BarChart supports categoryId and categoryLabel when stable layout identity differs from the visible label. Without categoryId, bars use deterministic index-based IDs so repeated labels do not collapse into one band.

<BarChart
  categoryId="id"
  categoryLabel="quarter"
  data={[
    { id: "north-q1", quarter: "Q1", revenue: 10 },
    { id: "south-q1", quarter: "Q1", revenue: 12 }
  ]}
  description="Both regions share the same displayed quarter label."
  title="Quarter revenue"
  x="quarter"
  y="revenue"
/>;

Styling

Load chart styles after @zvk/ui/styles.css when an app uses both packages:

import "@zvk/ui/styles.css";
import "@zvk/charts/styles.css";

The chart stylesheet still works without @zvk/ui; chart variables fall back to browser colors and package defaults. Customize through --zvk-chart-* variables instead of importing @zvk/ui internals.

Chart typography uses --zvk-chart-font-primary, --zvk-chart-font-secondary, and --zvk-chart-font-tertiary. These variables inherit --zvk-ui-font-family-primary, --zvk-ui-font-family-secondary, and --zvk-ui-font-family-tertiary when UI styles are present.

Server Safety

The root and React subpaths do not read window, document, ResizeObserver, matchMedia, or layout state at module initialization. Browser-only behavior belongs in a future client subpath with its own accessibility and interaction contract. Diagnostic callbacks are intentionally not part of the static React API because effects would make charts client-bound and render-time callbacks would be side effects.

Repo Skill

Use .codex/skills/use-zvk-charts/SKILL.md when maintaining this package.