Theming

Chart.Provider scopes a palette to a subtree. It writes what you pass as --zyplot-* custom properties on its own wrapper, and the charts inside read the resolved values back off the DOM. A key you leave out keeps the stylesheet's value, including that value's dark variant.

A theme key holds in both modes. The provider writes inline custom properties, which outrank the stylesheet's light and dark rules alike — so a color passed here is the color in both. When a palette has to change with the mode, set it in CSS instead.
tsx
<Chart.Provider
  theme={{
    colors: {
      categorical: ['#7c3aed', '#0284c7', '#ea580c'],
      grid: '#e5e7eb',
    },
    typography: {
      fontFamily: 'Geist, sans-serif',
    },
  }}
>
  <Dashboard />
</Chart.Provider>

The theme contract

Every key is optional and takes any color the browser can resolve. There is no background here — the box a chart sits in is surface, below.

This is the web shape. The native renderers take a ChartTheme of their own — no sequential, diverging, muted or border, and a background instead — and they accept it per chart as well as from the provider, because a native chart has no cascade to read a variable out of.
tsx
type ChartTheme = {
  colors?: {
    /** Slots 1…7, in order. A series takes one by index or by its own slot. */
    categorical?: string[]
    /** Low → high, five steps. Heatmap, treemap, sunburst. */
    sequential?: string[]
    /** Signed scales: diverging bars, and any positive/negative encoding. */
    diverging?: {
      negative?: string
      negativeSoft?: string
      neutral?: string
      positive?: string
      positiveSoft?: string
    }
    /** The de-emphasis grey — every series that is context rather than subject. */
    muted?: string
    axis?: string
    grid?: string
    /** Axis and data labels. */
    label?: string
    /** Tooltip fill. */
    surface?: string
    /** Tooltip hairline. */
    border?: string
    /** The unfilled part of a gauge or a meter. */
    track?: string
  }
  typography?: {
    /** A resolved family name. A canvas cannot read var(--font-sans). */
    fontFamily?: string
  }
}
Seven and five. Only the first seven categorical entries and the first five sequential steps are ever read. An eighth series color is one no color-blind reader can separate from a slot that already exists, so the eighth series is an "other" bucket, a small multiple, or a second encoding through texture — not a longer palette.

The chart surface

theme answers "what colour is this series"; surface answers "what does the container look like". Keeping them apart is what lets a design system set one card treatment for every chart while each chart keeps its own palette.

tsx
<Chart.Provider surface={{ background: '#fff', cornerRadius: 16, padding: 12 }}>
  <Chart.Line categories={categories} series={series} />
  <Chart.Bar surface={{ cornerRadius: 24 }} categories={categories} series={series} />
</Chart.Provider>

A chart's own surface merges over the provider's key by key, so the bar above rounds its corners without restating the background it inherits.

PropTypeDefaultDescription
backgroundstringFill behind the plot. Any CSS or hex colour.
border{ color?: string; width?: number }Outline around the container.
cornerRadiusnumber0Corner rounding, in px on web and points on native.
paddingnumber | ChartSurfacePaddingA number applies to all four sides; the object form takes horizontal, vertical and the individual sides, most specific winning.
The same four keys everywhere. Only properties that mean the same thing to a div, a SwiftUI view and a Compose Canvas live here. Anything that would have to be approximated on one of the three is deliberately absent.

Provider props

PropTypeDefaultDescription
children*ReactNodeCharts rendered inside the scope.
themeChartThemeScoped color and typography overrides.
surfaceChartSurfaceContainer treatment every chart in the subtree inherits, merged key by key with the chart's own.
colorMode"inherit" | "light" | "dark" | "system""inherit"How the light/dark palette is resolved for the subtree.
classNamestringCSS class on the wrapper element the provider renders.
The provider renders an element. The custom properties have to land somewhere, so the scope is a real div in your layout rather than context alone. It carries className for that reason.