Typed Canvas 2D plots for Askr applications, with the same immutable scene available for SVG and data export.
Version 0.1 is a clean break from the old CSS-first chart catalog. JavaScript comes from @askrjs/charts; structural and theme-token styles come from @askrjs/charts/styles. There are no component, core, default, per-chart CSS, template, or generator compatibility entrypoints.
npm install @askrjs/chartsImport the styles once at the application boundary:
@import "@askrjs/charts/styles";Create a typed plot namespace once at module scope, then compose marks inside its root:
import { createPlot } from "@askrjs/charts";
type RevenueRow = {
id: string;
day: Date;
revenue: number;
target: number;
};
const RevenuePlot = createPlot<RevenueRow>();
const revenue: readonly RevenueRow[] = [
{ id: "mon", day: new Date("2026-07-13T00:00:00Z"), revenue: 42, target: 48 },
{ id: "tue", day: new Date("2026-07-14T00:00:00Z"), revenue: 58, target: 50 },
{ id: "wed", day: new Date("2026-07-15T00:00:00Z"), revenue: 51, target: 52 },
];
export function RevenueTrend() {
return (
<RevenuePlot.Root
data={revenue}
rowKey="id"
label="Daily revenue"
title="Revenue"
description="Actual revenue and target for the current week."
>
<RevenuePlot.Bar x="day" y="revenue" />
<RevenuePlot.Line x="day" y="target" />
<RevenuePlot.Point x="day" y="target" />
</RevenuePlot.Root>
);
}Row drives field-name inference. A numeric mark channel cannot name a string field, and a factory's primitives cannot be mixed into another factory's root. Fields, accessors, and expressions can be combined without giving up row typing.
createPlot<Row>() returns a stable namespace containing:
- structure:
Root,Scale,Axis,Grid - marks:
Bar,Line,Area,Point,Arc,Cell,Rect,Rule,Text - interaction:
Legend,Tooltip,Crosshair,Select,Zoom,Brush
The root owns responsive sizing, semantic labels, title and description, empty and summary states, controlled or uncontrolled view and selection, follow-latest behavior, activation, and export access through onApiChange.
Useful defaults are inferred from channels and mark context:
- numbers use linear scales
Datevalues use local-time scales- categorical positions use band or point scales
- categorical colors use ordinal scales; numeric colors use continuous scales
- Cartesian marks receive default axes and tooltip behavior
Add explicit scale, axis, grid, legend, or tooltip children when the composition needs different behavior. Named scales support mixed plots and dual axes.
Tooltip supports nearest-mark inspection and shared nearest-x inspection with mode="mark" | "x"; auto chooses shared x for compatible Cartesian marks. Add Select for click/tap and Enter/Space selection. Its single default replaces the selection, while toggle adds or removes source keys. Selection updates before the additive onActivate(row, key, target) callback.
Mounted plots always respond to their container. width is the SSR and initial-layout fallback, not a fixed browser width; use a sized container for a fixed mounted chart.
- Signed finite numbers stay signed; negative values are not clamped to zero.
null,undefined, invalid dates, and non-finite numbers are missing values, not zero.- Log scales omit zero and negative values.
diagnosticsopts into omitted-value warnings, andsummaryreceivesomittedRowCountfor accessible reporting.- Stable row keys drive selection retention, transitions, and live updates.
- Use
constant("...")for a literal string channel. Bare strings identify row fields.
There are no compatibility wrappers in 0.1.
| Removed 0.0 surface | 0.1 composition | Worked example |
|---|---|---|
ChartShell |
Plot.Root; keep product card or page chrome in the app |
catalog |
ChartPanel |
App-owned card/section plus Plot.Root title and description |
catalog |
ChartEmptyState |
Root empty="..."; keep loading and error ownership in the route |
catalog |
ChartLegend |
Plot.Legend |
mark families |
AreaChart |
Plot.Area |
mark families |
BarChart |
Plot.Bar |
mark families |
LineChart |
Plot.Line, optionally with Plot.Point |
mark families |
DonutChart |
Plot.Arc with innerRadius |
mark families |
PieChart |
Plot.Arc with innerRadius={0} |
catalog recipes |
StackedBarChart |
Plot.Bar stack="series"; use normalize for percent stacks |
catalog recipes |
Sparkline |
Compact Plot.Root with Plot.Line or Plot.Area |
catalog recipes |
Heatmap |
Plot.Cell |
mark families |
Timeline |
Plot.Rule, Plot.Point, and Plot.Text |
mark families |
FlameGraph |
Plot.Rect with partition(...) |
mark families |
ProgressMeter |
Bounded Plot.Bar plus Root meter={{ role: "meter", ... }} |
mark families |
RadialGauge |
Bounded Plot.Arc plus Root meter={{ role: "meter", ... }} |
mark families |
@askrjs/charts/core |
Helpers exported directly from @askrjs/charts |
|
@askrjs/charts/default or root CSS import |
@askrjs/charts/styles |
|
Per-chart CSS, templates, and new:chart |
One structural stylesheet and primitive composition |
- Charting contract
- Architecture and defaults
- Usage recipes
- Mixed histogram and trend
- All mark families
- Live data, interactions, and export
@askrjs/themes is optional. The stylesheet defines self-contained --ak-chart-* tokens and uses compatible Askr theme tokens when they are present.