JavaScript / TypeScript package: flint-chart (packages/flint-js).
Python port: packages/flint-py is a source preview. Its input shape
mirrors the JS API, but PyPI publishing is planned for a later release.
Conceptual background: Overview · Pipeline: Architecture
- §1 Flint spec mapping
- §2 Assemblers
- §3 ChartAssemblyInput
- §4 Encodings and options
- §5 Complete example
- §6 Template discovery
- §7 Core utilities
- §8 Overflow and warnings
- §9 Subpath exports
- §10 Related
| Flint | API field | Contents |
|---|---|---|
| Raw table | data |
{ values: rows[] } or { url: "..." } |
| dataSpec | semantic_types |
field → string or field → SemanticAnnotation |
| chartSpec | chart_spec |
chartType, encodings, canvasSize, chartProperties |
Author semantic_types once per dataset and reuse it across charts. During most exploration, chart_spec is the only part that changes.
interface SemanticAnnotation {
semanticType: string;
intrinsicDomain?: [number, number]; // e.g. Rating [1, 5]
unit?: string; // e.g. USD, °C
sortOrder?: string[]; // custom ordinal order
}Bare string shorthand: "Price" is equivalent to { semanticType: "Price" }.
All backends accept the same ChartAssemblyInput and return a render-ready object.
import {
assembleVegaLite,
assembleECharts,
assembleChartjs,
assemblePlotly,
assembleExcel,
} from 'flint-chart';
const vlSpec = assembleVegaLite(input);
const ecSpec = assembleECharts(input);
const cjsSpec = assembleChartjs(input);
const plFig = assemblePlotly(input);
const xlSpec = assembleExcel(input);| Export | Returns |
|---|---|
assembleVegaLite |
Vega-Lite JSON spec |
assembleECharts |
ECharts option object |
assembleChartjs |
Chart.js configuration |
assemblePlotly |
Plotly.js { data, layout } figure |
assembleExcel |
Native Excel chart artifact for Office.js |
If a backend does not support a chartType, the assembler throws before render. Check support with vlGetTemplateDef, ecGetTemplateDef, cjsGetTemplateDef, plGetTemplateDef, or excelGetTemplateDef.
assembleExcel returns a serializable flint.excel.chart/v1 artifact. Render it
inside an Office.js Excel host, or turn it into standalone Office.js source:
import { assembleExcel, generateOfficeJs, renderExcelChart } from 'flint-chart';
const artifact = assembleExcel(input);
const { pngBase64, inspection } = await renderExcelChart(Excel, artifact, {
scale: 3,
cleanWorksheet: true,
inspectNativeChart: false,
});
const { code, meta } = generateOfficeJs(artifact, {
scale: 3,
cleanWorksheet: true,
functionName: 'renderFlintChart',
});renderExcelChart requires an Excel API object with run and
ImageFittingMode.fit. It creates the native chart on the active worksheet and
returns a PNG capture plus optional inspection data. generateOfficeJs does not
execute Excel; it returns portable source and metadata about the generated data
range and chart.
interface ChartAssemblyInput {
data: { values: Record<string, unknown>[] } | { url: string };
semantic_types?: Record<string, string | SemanticAnnotation>;
chart_spec: {
chartType: string;
title?: string; // headline
subtitle?: string; // deck: what is measured, of whom, when, in what units
encodings: Record<string, ChartEncoding | string>; // string = field shorthand
baseSize?: { width: number; height: number }; // target layout size, default 400×320
canvasSize?: { width: number; height: number }; // optional hard ceiling on stretch
chartProperties?: Record<string, unknown>;
};
options?: AssembleOptions;
field_display_names?: Record<string, string>;
}| Form | Description |
|---|---|
{ values: rows[] } |
Inline row objects (editor and tutorials) |
{ url: "..." } |
Remote JSON or CSV URL |
Maps column name → semantic type. This drives encoding type, formatting, aggregation defaults, color class, and layout. See Semantic Type.
Maps raw column names to readable presentation labels used for axis titles and legend headers. Keep encodings bound to the original field names:
{
field_display_names: {
percentageOfCountries: 'Percentage of countries'
},
chart_spec: {
chartType: 'Bar Chart',
encodings: { x: 'country', y: 'percentageOfCountries' }
}
}| Field | Description |
|---|---|
chartType |
Template name — must match a backend registry entry ("Bar Chart", "Heatmap", …) |
title |
The headline. Write one: Jan and Cairo name their own kind, 26 and 5,300 do not, and a theme that omits axis titles is delegating that naming to the headline. Vega-Lite only for now; where no headline is given, the compiler puts the axis titles back. |
subtitle |
The deck — what is measured, of whom, when, in what units. |
encodings |
Channel → encoding map |
baseSize |
Target layout size in pixels (default 400×320): the size the chart aims for with typical data. Dense data may stretch past it, up to the ceiling. |
canvasSize |
Hard ceiling: the maximum size the chart may ever reach, including faceted grids. If omitted, the ceiling is baseSize × options.maxStretch (default 1.5×). Per-dimension caps are βx = canvasSize.width / baseSize.width, βy = canvasSize.height / baseSize.height (each ≥ 1). The base is clamped to the ceiling, so a canvasSize on its own acts as a fixed box the chart fills and shrinks to fit without overflowing. |
chartProperties |
Template-specific toggles (e.g. orient, opacity) |
base vs. canvas, in one line:
baseSizeis what the chart aims for;canvasSizeis what it may never exceed. UsecanvasSizefor a fixed slot, andbaseSizefor a comfortable target that may grow for dense data. See the Example: Auto Layout.
interface ChartEncoding {
field?: string;
type?: 'quantitative' | 'nominal' | 'ordinal' | 'temporal';
aggregate?: 'count' | 'sum' | 'average' | 'mean';
sortOrder?: 'ascending' | 'descending';
sortBy?: string;
scheme?: string;
}Explicit type overrides semantic inference. Setting aggregate asks Flint to
collapse the rows itself — grouping by the other (non-aggregated) field channels
and producing a derived column named ${field}_${aggregate} (count →
_count). average and mean are synonyms. Most callers should still
aggregate their data upstream; if you do, omit aggregate and reference the
derived column by name.
Common channels: x, y, color, size, shape, column, row, group, detail.
interface AssembleOptions {
addTooltips?: boolean; // default false
elasticity?: number; // discrete stretch exponent (default 0.5)
maxStretch?: number; // default stretch cap when no canvasSize ceiling (default 1.5)
maxStretchX?: number; // per-dimension width cap (derived from canvasSize)
maxStretchY?: number; // per-dimension height cap (derived from canvasSize)
facetElasticity?: number; // facet stretch (default 0.3)
minStep?: number; // min px per discrete item (default 6)
minSubplotSize?: number; // min facet subplot px (default 60)
maxColorValues?: number; // color cardinality before truncation (default 24)
stepPadding?: number; // band inner padding fraction (default 0.1)
defaultBandSize?: number; // baseline px per category (backend-tuned)
maxBandSize?: number; // max px per category when sparse (backend-tuned)
baseLabelFontSize?: number; // axis tick font at the reference canvas (VL 10, EC/CJS/Plotly 12)
baseTitleFontSize?: number; // header font — axis title / legend / chart title (VL 11, EC/CJS 12, Plotly 14)
}Fonts adapt to canvas size around these per-backend bases: at the default
canvas each backend renders its native size, growing subtly on larger canvases
(up to base + 4) and shrinking in small-multiple subplots; axis tick labels also
shrink/rotate/truncate to avoid overlap. Set baseLabelFontSize /
baseTitleFontSize to scale all text up or down — e.g. baseLabelFontSize: 14
for larger tick labels across the whole chart.
Full list: packages/flint-js/src/core/types.ts (AssembleOptions). Behavior: Auto Layout Algorithm.
const input: ChartAssemblyInput = {
data: {
values: [
{ quarter: 'Q1', revenue: 1200 },
{ quarter: 'Q2', revenue: 1450 },
{ quarter: 'Q3', revenue: 980 },
{ quarter: 'Q4', revenue: 1800 },
],
},
semantic_types: { quarter: 'Quarter', revenue: 'Price' },
chart_spec: {
chartType: 'Bar Chart',
encodings: {
x: { field: 'quarter' },
y: { field: 'revenue' },
},
baseSize: { width: 480, height: 320 },
},
};
const spec = assembleVegaLite(input);import {
vlTemplateDefs,
vlGetTemplateDef,
vlGetTemplateChannels,
ecGetTemplateDef,
cjsGetTemplateDef,
} from 'flint-chart';
Object.keys(vlTemplateDefs);
// ["Points", "Bars", "Lines & Areas", …]
vlGetTemplateChannels('Scatter Plot');
// ["x", "y", "color", "size", "opacity", "column", "row"]Re-exported from flint-chart and flint-chart/core:
| Symbol | Purpose |
|---|---|
inferVisCategory |
Infer coarse vis category from raw data |
getVisCategory |
Look up category for a semantic type string |
getRegistryEntry |
Query TypeRegistryEntry for a type |
channels, channelGroups |
Channel metadata |
Key types: ChartAssemblyInput, ChartEncoding, ChartTemplateDef, AssembleOptions, ChartWarning, ChannelSemantics.
When a discrete channel exceeds the layout budget, the compiler:
- Computes how many items fit (Auto Layout Algorithm §2)
- Applies the template overflow strategy
- Filters data to kept values
- Attaches warnings to the result
Default strategy priority:
- Connected marks (line, area) — keep all points
- User-specified sort — keep top/bottom N
- Opposite quantitative axis — sort and truncate
- Bar + count — sum-aggregate then truncate
- Numeric field — numeric sort, first N
- Fallback — first N in data order
Inspect _warnings or ChartWarning arrays in integration code to surface truncation in your UI.
| Import path | Contents |
|---|---|
flint-chart |
Assemblers + main re-exports |
flint-chart/core |
Types, semantics, layout |
flint-chart/vegalite |
VL templates and assembleVegaLite |
flint-chart/echarts |
ECharts templates and assembleECharts |
flint-chart/chartjs |
Chart.js templates and assembleChartjs |
flint-chart/test-data |
Gallery generators (TEST_GENERATORS) |
- Overview — dataSpec + chartSpec motivation
- Architecture — three-stage pipeline
- Semantic Type — type hierarchy and resolution
- Getting started — hands-on walkthrough
- Extending backends — new
assemble*()target