@datafe-open/markdown-chart-kpi
Strict, framework-neutral KPI card renderer for
@datafe-open/markdown-chart.
Metrics share a single outer chart frame with whitespace between items, plain status text, and a compact Chart/Data toolbar. Explicit format prefixes and suffixes use smaller type without changing their text or spaces. Line and area sparklines stay 44px high; metrics without trends need no reserved trend space.
import { ChartRendererRegistry } from '@datafe-open/markdown-chart';
import { createKpiRenderer } from '@datafe-open/markdown-chart-kpi';
const registry = new ChartRendererRegistry().register(createKpiRenderer());
The canonical fence keeps default or named datasets separate from renderer-owned field, format, trend, status, and reference configuration:
```markdown-chart
{
"version": 1,
"renderer": "kpi",
"data": {
"kind": "inline",
"source": [
{ "day": "2026-08-31", "revenue": 16800000, "tone": "warning" },
{ "day": "2026-09-01", "revenue": 18000000, "tone": "positive" }
]
},
"spec": {
"colorConvention": "green-up-red-down",
"timeField": "day",
"items": [
{
"id": "revenue",
"title": "Revenue",
"value": {
"field": "revenue",
"reduce": "lastNonNull",
"format": {
"style": "currency",
"currency": "CNY",
"notation": "compact",
"maximumFractionDigits": 1
}
},
"status": {
"text": { "literal": "Above target" },
"tone": { "field": "tone" }
},
"trend": {
"type": "area",
"compare": {
"lag": 1,
"mode": "relative",
"label": "vs previous day",
"polarity": "higher-is-better"
}
},
"references": [
{ "ref": "docs://metrics/revenue", "label": "Revenue definition" }
]
}
]
}
}
```
value.field reads from canonical data and defaults to lastNonNull reduction.
Formatting uses a validated subset of Intl.NumberFormat: text, decimal,
percent, currency, or unit, plus notation, fraction digits, prefix,
suffix, and null display. Functions and arbitrary expressions are rejected.
Items use the default top-level data unless they set dataset to a key in
the top-level datasets map. Prefer shared default data for matching grain,
filters, and source; use named datasets when those differ. Each selected
inline/ref dataset is materialized once and total row/cell limits apply across
the selected collection. The default data remains the source of the built-in
Data table; a named-only group renders without that single-dataset toggle.
{
"data": { "kind": "inline", "source": [{ "revenue": 18000000 }] },
"datasets": {
"inventory": { "kind": "inline", "source": [{ "stock": 23 }] }
},
"spec": {
"items": [
{ "id": "revenue", "title": "Revenue", "value": { "field": "revenue" } },
{ "id": "inventory", "title": "Inventory", "dataset": "inventory", "value": { "field": "stock" } }
]
}
}
An item may omit trend, or use a line/area sparkline with an exact source
row lag, absolute/relative comparison, polarity, and optional zero-inclusive Y
scale. Fewer than two valid numeric points hides the trend without failing the
KPI. Source order is authoritative; null lag rows are not skipped.
Referenced canonical data remains host-owned. Pass a resolver to this renderer
directly or through the React/Vue adapter's kpi option:
const renderer = createKpiRenderer({
validateDataRef: (ref) => ref.startsWith('dataset://'),
resolveDataRef: (ref, { signal }) => loadDataset(ref, signal),
referenceIcon: ({ document }) => createHostReferenceIcon(document),
});
The resolved dataset is used once for values, trends, comparisons, and the shared Data table. The renderer never fetches or interprets refs itself.
References are separate opaque item configuration and only become controls
when the host supplies referenceActions:
const referenceActions = {
canOpen: ({ reference }: { reference: { ref: string } }) =>
reference.ref.startsWith('docs://'),
open: ({ reference }: { reference: { ref: string } }) => {
openDocumentation(reference.ref);
},
};
referenceIcon is a trusted host-only presentation hook. It receives the
opaque reference event plus the control's owner document and returns a newly
created decorative HTML/SVG element. If the hook is absent, returns nothing, or
throws, the renderer uses its default link glyph. The renderer does not assign
domain meaning to either icon.
Hosts can align the renderer with their theme through the
--markdown-chart-kpi-* CSS custom properties. Unset properties have light and
dark fallbacks selected from the chart mount theme.
Direction colors
Set spec.colorConvention to red-up-green-down for Chinese output or
green-up-red-down for English output. Comparison text and its line/area
sparkline use the actual delta direction, independent of polarity. Zero uses
neutral gray; missing/undefined comparisons keep the ordinary accent sparkline.
Status tones still describe business status. Without this optional field, old
messages retain polarity-based comparison colors and accent sparklines.
The stored convention controls replay; browser language does not change it.
Direction colors support --markdown-chart-kpi-red and
--markdown-chart-kpi-green, with light/dark defaults; status color variables
are unchanged. Upgrade the renderer before enabling Skill output with this field.