Semantic and Graphical State

ggaction keeps chart meaning separate from its fully resolved graphics.

semanticSpec = what the chart means
graphicSpec  = what the renderer draws

Semantic state

semanticSpec stores named datasets, semantic layers and marks, data-driven encodings, scales, coordinates, and guides. Examples include:

Graphical state

graphicSpec contains backend-neutral concrete nodes such as canvas, circle, line, and text. Every stored x/y coordinate, radius, color, line endpoint, and label string is already resolved.

Named graphics form an explicit ownership tree. A normal Canvas-first chart has one Canvas root, a plot container for grids, marks, and axes, then Canvas-owned legends and titles:

canvas
├─ plot
│  ├─ grid
│  ├─ marks
│  └─ axes
├─ legends
└─ title

Sibling order is drawing order. Containers do not imply clipping, transforms, or layout; they only record ownership and order. Repeated concrete mark items remain in the owning graphic’s items collection rather than becoming named tree children.

See Inspecting authored and materialized state for the exact semanticSpec.layers, graphicSpec.objects[id].items, ownership, and drawing-order access paths.

It never stores field expressions, scale calls, executable functions, or instructions for a renderer to infer.

No automatic compiler

There is no automatic semantic-to-graphic compilation step. A domain action explicitly updates semantic state and invokes the graphical actions required to materialize that change.

For example, encodeX records the field and scale, resolves every consumer, and writes concrete x values. render() later reads only graphicSpec.

Appearance-only values such as canvas background, fixed radius, fonts, and strokes are graphical. User-authored scale domains and ranges remain semantic; the values produced by applying them are graphical.