styles(...) part in, the same way you pipe config(...).
Theme tokens (the built-in light/dark chrome, selected by
colorScheme)
are a separate surface covering the HTML chrome around the plot. 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.
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
Callstyle.<target>(declarations, options?). Geom targets accept conditions; chrome and annotation
targets are chart-scoped and take declarations only.
Three things worth knowing up front:
- A bar’s
borderRadiusis a token:'none' | 'xs' | 'sm' | 'md' | 'lg' | 'xl' | 'full', defaulting to'sm'. OndataLabel,panelBorder,style.graphandstyle.geom.tileit is a plain pixel number. style.graph({ fontFamily })is the chart’s base family: every text region without a family of its own follows it, in paint and in measurement.style.graph({ textScale })multiplies every text size at resolve time.1is the built-in; a preset can set a house scale throughextends.gap,offset, padding and stroke widths do not scale.style.graph({ padding })is the outer frame, in pixels, and does not followtextScale. A number applies to every side; an object sets sides, and omitted edges keep the builtin 24.- Hide a panel-border edge with
strokeWidth: 0. - A shape annotation’s border wears its
colorunlessborderColoris declared;borderWidth: 0draws none, andcolor: 'transparent'leaves a stroke-only outline.{ annotation: id }addresses one annotation. - An arrow annotation’s
borderWidthandshadoware its sticker look — an outline around the line and a drop shadow; both are off by default. - A difference arrow is two parts: the line (
strokeWidthalso sizes the route around the observations) and its label,style.annotation.differenceArrow.label. A colour no entry names falls to the series colour both ends share; the label’s box border takes the arrow’s colour unlessborderColoris declared. - A text annotation’s
backgroundis its box; with none declared there is no box. The content’s own marks paint over the base type. An image’sborderRadiusclips its corners, in pixels. - A pinned number and a comment are a marker dot plus the label bubble beside it (
.label). A marker whosecolorno entry names borrows its observation’s colour; the swatch inside a pinned-number bubble always keeps it. - Stack totals (
.aggregate) always sit outside the mark, so they take no.inside/.outside. style.headlineItemis not callable — pick a part..number.centeris the donut-hole figure; without it the hole uses.number.gaponstyle.headlineandstyle.legendis pixels and does not followtextScale. A barestyle.legendentry is gap only; the overflow popover box isstyle.legend.popover(background,borderColor,borderWidth,borderRadius,paddingInline,paddingBlock,shadow).- Swatch fill opacity and bar corner radius follow the layer geom, including size-legend bubbles;
style.legendItem.swatchandstyle.headlineItem.swatchonly size the square box.
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 awhere 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; the y grid
lines dashed; the panel border dashed with borderRadius: 6; the graph frame 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 and textSecondary name a stylesheet token and a theme token.
They are different values in different namespaces: the stylesheet tokens above drive the plot, while
the theme tokens of the same name drive the chrome around it.
When an entry is invalid
An entry the engine can’t use is reported as anINVALID_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:
colorScheme yourself — it defaults to 'light', and no exported hook carries the chart’s
active scheme into a renderer.
