> ## Documentation Index
> Fetch the complete documentation index at: https://docs.graphy.dev/llms.txt
> Use this file to discover all available pages before exploring further.

# Theming

A **theme** is the set of CSS custom properties the renderer writes onto the chart wrapper. It covers the HTML chrome *around* the plot — legend, tooltip, headline, footer, editor UI. Everything the chart itself draws, its font families included, is on the [Styling](/sdk-next/config/styling) page.

| What you want to change | Where it lives |
| - | - |
| Marks, grid lines, tick marks, panel border, graph background, base and heading font families, axis/tick/data-label type | `styles()` on the spec — [Styling](/sdk-next/config/styling) |
| Legend, tooltip, headline, footer, editor UI | the built-in theme, selected by `colorScheme` |
| The overlay menu in `'point-and-edit'` mode | editor menu tokens, [below](#overlay-menu) |
| Editor tooltips | editor tooltip tokens, [below](#editor-tooltips) |
| Series colors | the color scale, plus `customPalettes` |

## Color scheme

`colorScheme` picks the base token set:

```tsx theme={null}
<GraphProvider spec={spec} data={data} colorScheme="dark">
  <GraphRenderer />
</GraphProvider>
```

<ParamField path="colorScheme" type="ColorScheme" default="light">
  `'light'` or `'dark'`, exported from `@graphysdk/viz-engine`.
</ParamField>

One prop, two surfaces: the same value decides which half of every `{ light, dark }` stylesheet color and every `token()` pair resolves inside the plot, and which tones the [default palette](/sdk-next/concepts/scales#categorical-color) spreads over touching geoms. A chart switches scheme in one place.

The token contract itself is exported as `vars`, for hosts that would rather write their own CSS against the custom properties:

```tsx theme={null}
import { vars } from '@graphysdk/react';
```

## Fonts

Font families reach the chart from the stylesheet:

* `style.graph({ fontFamily })` in the stylesheet — the chart's base family; every text region without a family of its own follows it, in paint and in measurement.
* `style.heading({ fontFamily })` — the title and subtitle in the content region (both levels; `style.heading.h1` / `.h2` stamp one). Undeclared, they follow the graph entry. Size, weight, colour and line-height live here too; `style.caption` and `style.source` are the footer type targets.
* `style.axisLabel`, `style.tickLabel`, `style.dataLabel`, `style.legendItem`, `style.directLabel` and `style.geom.rule.label`, each taking a `fontFamily` — the type inside the plot and on legend pills. See [Styling](/sdk-next/config/styling).

<Note>
  The SDK references fonts, it doesn't load them. Load them on the page (via
  `@font-face` or a font service) **before the first measurement** — text
  measured while a family is still loading is measured with fallback metrics,
  and the layout keeps those numbers.
</Note>

## What the chrome tokens cover

| Category | Tokens |
| - | - |
| Legend | `legendSwatchGap` |
| Headline | `headlineRowGap` |
| Annotations | `annotationOutlineColor` |
| Base typography | `fontFamily`, `fontFamilyHeading`, `fontWeight*`, `fontSize*`, `fontLineHeight*`, plus per-element tokens such as `fontButton` (chart families and content type are stylesheet-owned: `style.graph`, `style.heading`, `style.caption`, `style.source`) |
| Semantic colors | `textPrimary`, `textSecondary`, `textDisabled`, `iconPrimary`, `iconSecondary`, `border100/50/10`, `defaultBackground`, `raisedBackground`, `sunkenBackground`, `overlayBackground` |
| Elevation, radii, space | `elevationXs…Lg`, `radiiXs…Lg`, `spaceXxs…Xl`, `zIndex*` |
| Editor UI | `toolbarBackgroundColor`, `toolbarButtonBackgroundColor*`, `toolbarSeparatorColor`, `editorControlHeight`, `fontEditor*` |

## Overlay menu

The overlay menu, which `'point-and-edit'` mode opens beside a selection, wears Graphy's own look in light and dark, apart from the built-in theme's, and keeps the theme's font and text scale whatever the stylesheet sets. A host restyles the menu alone through the **editor menu tokens**: CSS variables no theme declares, so a value set on `:root` reaches the menu. A page that places a `<div id="graphy_root">`, where the chart then portals its menus, can set them on any element holding it instead. Set literal values, since a `var(--graphy-*)` set on `:root` resolves there, before any theme, and the menu falls back to its own.

The controls in the menu's rows are compact and take their colors from the [editor look](#editor-look). The editor menu tokens do not restyle them, and neither does the theme's `editorControlHeight`: the menu sets its own control height, so that token reaches the editor panel alone.

Every portal the chart opens carries its color scheme as `data-graphy-color-scheme`, `light` or `dark`, so a rule on it gives one scheme's menus values of their own. It is the only way to tell charts apart: every chart on the page portals its menu into the same container, so a value set on a chart's own element never reaches its menu.

```css theme={null}
:root {
  --graphy-editor-menu-background: #fbfaf4;
  --graphy-editor-menu-text: #1f1e1e;
  --graphy-editor-menu-border: #1f1e1e;
  --graphy-editor-menu-radius: 0;
  --graphy-editor-menu-shadow: 4px 4px 0 #1f1e1e;
}

[data-graphy-color-scheme='dark'] {
  --graphy-editor-menu-background: #1f1e1e;
  --graphy-editor-menu-text: #fbfaf4;
  --graphy-editor-menu-border: #fbfaf4;
  --graphy-editor-menu-shadow: 4px 4px 0 #fbfaf4;
}
```

| Token (`--graphy-editor-menu-*`) | Takes | Unset, the menu reads |
| - | - | - |
| `background` | a color | the editor look's `surface` |
| `text` | a color | the editor look's `text` |
| `border` | a color | the editor look's `text` at 12% |
| `radius` | a length | `radiiMd` |
| `shadow` | a `box-shadow` | `elevationMd` and a float shadow |
| `padding` | a length | `spaceMd` |
| `title-font` | a `font` shorthand | `600 13px/1.3` in the theme's font |
| `min-width`, `max-width` | a length | 252px |

A value set without a scheme holds in light and dark alike, so set `text` whenever you set `background`, and `border` whenever you set `text`: the border follows the editor look's text, not the menu's. An unset `min-width` takes `max-width`, so setting the max alone sets the menu's width, narrower or wider. The room beside the selection still caps both widths.

## Editor look

The editor panel's controls and the overlay menu share one look: warm ink on a light or a dark surface, with ink as the accent of a switch, a slider and a selected option. The built-in theme carries it apart from the chart's own tokens, so the footer, the headline and the difference arrows do not move with it. A host changes it for both with the **editor tokens**, CSS variables no theme declares. Set them on `:root`, or on an element that holds both the panel and `#graphy_root`, where the menu portals: an element that holds only the chart or only the panel reaches one of them.

```css theme={null}
.host-editor {
  --graphy-editor-accent: #0b5fff;
  --graphy-editor-surface: #fff8e6;
  --graphy-editor-text: #2b2b2b;
  --graphy-editor-border: #d9b86c;
  --graphy-editor-font-family: Georgia, serif;
  --graphy-editor-padding-x: 12px;
  --graphy-editor-padding-y: 8px;
}
```

| Token (`--graphy-editor-*`) | Takes | Unset, the editor reads |
| - | - | - |
| `accent` | a color | ink, `#3B372F` or `#ECE7DF` in dark |
| `surface` | a color | `#FFFFFF`, or `#26241F` in dark |
| `text` | a color | ink, `#3B372F` or `#ECE7DF` in dark |
| `border` | a color | `text` at 12% for dividers, fields and the menu; the panel draws none |
| `font-family` | a font family | the theme's |
| `padding-x`, `padding-y` | a length | `spaceMd`, 8px, for the panel and the menu |

The controls' text, their sunken and raised surfaces, their borders and the focus ring are worked out from `surface` and `text`, so set the two together. `border` outlines the panel only when you set it. The panel now pads its content by 8px; a host whose own box already pads it sets `padding-x` and `padding-y` to `0px`. A React host sets the tokens with a `style` on the element that holds `#graphy_root`. The editor panel's controls were brand-colored before this look. A host that wants that accent back sets `accent`. A select's list, which opens in a popup of its own, takes the look's colors too, its highlighted and selected rows included.

## Editor tooltips

Every tooltip the editor draws, on a control's option or on a toolbar button, reads the **editor tooltip tokens** the way the overlay menu reads its own: set them on `:root`, around `#graphy_root` or on one `data-graphy-color-scheme`, as literal values.

| Token (`--graphy-editor-tooltip-*`) | Takes | Unset, the tooltip reads |
| - | - | - |
| `background` | a color | the [editor look](#editor-look)'s `text` |
| `text` | a color | the editor look's `surface` |
| `radius` | a length | `radiiXs` |
| `font` | a `font` shorthand | `fontUITooltipSecondary`, in the editor look's `font-family` |

## Structural and paint-only tokens

Most tokens only paint. A handful also move layout, because the layout solve reads them in JS and reserves space from the same numbers:

`legendSwatchGap` and `headlineRowGap`.

Overriding one of those moves reserved space and paint together. Give them a plain pixel length — a value in any other unit is skipped by the layout solve, which keeps the built-in number instead, and reserved space then disagrees with what paints.

## Tokens that carry no paint

A few token names describe paint the stylesheet owns. Reach for the style entry instead.

| Token | What actually paints it |
| - | - |
| `graphBackground` | `style.graph({ fill })` |

`graphBackground` is worth calling out: the renderer resolves the stylesheet's `graph` background and writes it into the CSS variable, so the stylesheet value is what a chart paints.

```tsx theme={null}
// paints the background
styles({ defaults: [style.graph({ fill: '#0B1020' })] });
```

## Two token namespaces

`textPrimary` and `textSecondary` each name a theme token **and** a [stylesheet](/sdk-next/config/styling) token. They are independent tables: the theme's pair colors HTML chrome text, while the stylesheet's `textPrimary` paints axis and data labels and its `textSecondary` paints tick labels. If you set one by name and nothing moves, check which namespace you reached for.

## Series colors

Series colors are not theme tokens. They come from the chart's color scale, and `customPalettes` on `GraphProvider` supplies the host-owned palettes a scale can reference by id:

```tsx theme={null}
<GraphProvider
  spec={spec}
  data={data}
  customPalettes={{
    brand: [
      { id: 'primary', hex: '#0B5FFF' },
      { id: 'accent', hex: '#E5484D' },
    ],
  }}
>
  <GraphRenderer />
</GraphProvider>
```

```tsx theme={null}
scale.color.palette({ palette: { type: 'custom', id: 'brand' } });
```

A stylesheet `overrides` entry that declares `fill` or `stroke` sits above the scale, so it wins over the palette. See [Styling](/sdk-next/config/styling).

## Related

* [Styling](/sdk-next/config/styling) — the stylesheet on the spec: marks, grid, axes, background
* [Provider & renderer](/sdk-next/rendering/provider-and-renderer) — where `colorScheme` and `customPalettes` are set


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.