Skip to content

OlliVisSpec

OlliVisSpec is Olli's normalized description of a data visualization. It comes in two forms: UnitOlliVisSpec for a single chart, and MultiOlliVisSpec for layered or concatenated views.

You rarely write an OlliVisSpec by hand — the Vega-Lite adapter generates it for you. But understanding the shape is useful for debugging, writing tests, or building a custom adapter.

UnitOlliVisSpec

A single chart view.

ts
interface UnitOlliVisSpec {
  data: OlliDataset;
  fields?: OlliFieldDef[];
  structure?: OlliNode | OlliNode[];
  mark?: OlliMark;
  axes?: OlliAxis[];
  legends?: OlliLegend[];
  guides?: OlliGuide[];
  facet?: string;
  selection?: Selection;
  title?: string;
  description?: string;
}
PropertyTypeRequiredDescription
dataOlliDatum[]yesThe dataset. Each datum is a Record<string, OlliValue>.
fieldsOlliFieldDef[]noField definitions with types, labels, binning, time units. Auto-inferred if omitted.
structureOlliNode | OlliNode[]noHow the tree is organized — groupby, predicate, and annotation nodes. Auto-inferred from axes/legends if omitted.
markOlliMarknoA mark type string or OlliMarkDef object. Used for chart-type descriptions. See OlliMark.
axesOlliAxis[]noX and Y axis guides with field, title, scale type, and tick values.
legendsOlliLegend[]noColor, opacity, or size legend guides.
guidesOlliGuide[]noGeneric guides that don't fit axis or legend.
facetstringnoField name for faceting. The top-level groupby becomes facet views.
selectionSelectionnoPre-filter applied to the data before lowering.
titlestringnoChart title.
descriptionstringnoLonger description, announced at the root node.

MultiOlliVisSpec

Multiple views composed together.

ts
interface MultiOlliVisSpec {
  operator: 'layer' | 'concat';
  units: UnitOlliVisSpec[];
}
PropertyTypeRequiredDescription
operator'layer' | 'concat'yes'layer' overlays views (same axes). 'concat' places them side by side.
unitsUnitOlliVisSpec[]yesThe individual views.

Type guard

ts
function isMultiSpec(spec: OlliVisSpec): spec is MultiOlliVisSpec

OlliMark

ts
type OlliMarkType = 'point' | 'bar' | 'line' | 'area' | 'rect' | 'tick' | 'arc';

interface OlliMarkDef {
  type: OlliMarkType;
  innerRadius?: number;
  stack?: 'stacked' | 'grouped';
}

type OlliMark = OlliMarkType | OlliMarkDef;

The mark can be a plain type string ('bar') or an object with additional properties ({ type: 'arc', innerRadius: 50 }). Mark-level properties like innerRadius and stack live on the OlliMarkDef object rather than the top-level spec.

Use the getMarkType helper to extract the type string from either form:

ts
import { getMarkType } from 'olli-vis';

getMarkType('bar');                          // 'bar'
getMarkType({ type: 'arc', innerRadius: 50 }); // 'arc'
getMarkType(undefined);                      // undefined

The mark type drives chart-type inference in description tokens. A point mark with two quantitative axes becomes "scatterplot"; a rect with a color legend becomes "heatmap"; an arc mark becomes "pie chart" or "donut chart" depending on innerRadius.

Value types

ts
type OlliValue = string | number | Date;
type OlliDatum = Record<string, OlliValue>;
type OlliDataset = OlliDatum[];

Elaboration

When you call olliVis or lowerVisSpec, the spec is first passed through elaborateSpec:

ts
function elaborateSpec(spec: OlliVisSpec): OlliVisSpec

Elaboration fills in defaults:

  1. If fields is missing, it creates one OlliFieldDef per key in data[0].
  2. If any field lacks a type, it runs type inference on the data column.
  3. If structure is missing, it infers a tree structure from the axes, legends, and guides.

This means a minimal spec only needs data, mark, and axes — everything else is derived.

Next

Released under the BSD-3-Clause License.