Skip to main content
Everything the chart draws — marks, grid lines, tick marks, the panel border, the graph background, axis and tick and data-label type — is painted from a stylesheet on the spec. You add one by piping a styles(...) part in, the same way you pipe config(...).
Because it lives on the spec, a stylesheet is serializable and travels with the chart — the same as mappings, scales and config.
Theme tokens (themeOverrides on GraphProvider) are a separate surface covering the HTML chrome around the plot: legend, tooltip, headline, footer. See Theming.

The four keys

StyleRule[]
Applied where no mapped aesthetic decided the value — the look when nothing else speaks.
StyleRule[]
Applied over whatever a mapping decided.
Record<string, StyleTokenValue>
Named colours that entries reference with token('name').
Stylesheet[]
Composes other stylesheets underneath this one. Tokens merge name by name, lists concatenate, later wins. This is how a house style ships as a reusable preset.
Piping several styles(...) parts stacks them in order, each sitting above everything piped before it. Within a single list, order is specificity: the last matching entry that declares a property wins.

How a value is resolved

Each property resolves through three tiers, in order: The practical consequence: defaults never fight your mappings, and overrides always do. To recolour a series that is mapped to color, you need an overrides entry — a defaults entry loses to the scale. Entries scoped to a state ({ state: 'hovered' | 'dimmed' }) sit above the whole stateless cascade.

Targets

Call style.<target>(declarations, options?). Geom targets accept conditions; chrome targets are chart-scoped and take declarations only. Three things worth knowing up front:
  • A bar’s borderRadius is a token: 'none' | 'xs' | 'sm' | 'md' | 'lg' | 'xl' | 'full', defaulting to 'sm'. On dataLabel, panelBorder and style.graph it is a plain pixel number.
  • Hide a panel-border edge with strokeWidth: 0.
  • Stack totals (.aggregate) always sit outside the mark, so they take no .inside / .outside.

Colours

Any colour-valued property takes one of three forms:
{ light, dark } and token(...) resolve against the provider’s colorScheme. Prefer them to literals — a literal looks the same in both schemes.

Conditions

Geom entries take a where predicate (the same language as highlights), a state, and a layer:
layer scopes an entry to one authored layer id — the way to style a single series of a combo chart without touching the others.

Re-skinning through tokens

The engine’s built-in stylesheet sits behind every chart, and its defaults are written in terms of tokens. Redefining a built-in token name restyles the default it backs, with no entries at all:
The built-in defaults these produce: bars borderRadius: 'sm' with a 1px border; lines and areas strokeWidth: 2, lineType: 'solid'; areas alpha: 0.3; points size: 8; rules dashed; grid lines dashed; the panel border dashed with borderRadius: 8; tick labels offset 10px; the dimmed state alpha: 0.4.

Presets with extends

extends composes stylesheets, which is how a house style becomes reusable:

Two token namespaces

textPrimary, textSecondary and gridLineColor name a stylesheet token and a theme token. They are different values in different namespaces: the stylesheet tokens above drive the plot, while the themeOverrides keys of the same name drive the chrome around it. If setting one by name has no visible effect, check which namespace you reached for.

When an entry is invalid

An entry the engine can’t use is reported as an INVALID_STYLE_RULE warning and skipped — the chart still renders. Compile a spec headlessly to see warnings before wiring it into React.

Custom geoms

A custom geom renderer reads paint through the value accessors on its render input (getColor, getAlpha, getSize, …), which expose the data tier. To resolve the full cascade, build a resolver over the layer:
Supply colorScheme yourself — it defaults to 'light', and no exported hook carries the chart’s active scheme into a renderer.