# Chart Spec

Every static definition and dynamic chart builder resolves to a `ChartSpec`.
The spec owns chart composition, scale factories or fixed scale instances, and
presentation.

```ts
interface ChartSpec<TMarks extends readonly ChartMark[]> {
  marks: TMarks
  x: ChartAxisOptions | null
  y: ChartAxisOptions | null
  guides?: boolean
  color?: ChartColorOptions
  gradients?: readonly ChartLinearGradient[]
  clip?: boolean
  margin?: number | Partial<ChartMargin>
  theme?: Partial<ChartTheme>
}
```

## Properties

| Property    | Required | Meaning                                                                                                            |
| ----------- | -------- | ------------------------------------------------------------------------------------------------------------------ |
| `marks`     | Yes      | Ordered mark layers. Later scene nodes paint after earlier ones.                                                   |
| `x`         | Yes      | X scale and guide options, or `null` when no mark uses an x scale.                                                 |
| `y`         | Yes      | Y scale and guide options, or `null` when no mark uses a y scale.                                                  |
| `guides`    | No       | Set to `false` to suppress both axes, grid lines, titles, and their implicit margins.                              |
| `color`     | No       | Shared categorical or custom color scale and optional legend.                                                      |
| `gradients` | No       | Linear-gradient resources consumed by the resource-aware SVG renderer.                                             |
| `clip`      | No       | Clips the marks group to the resolved inner chart bounds when the selected renderer supports resources.            |
| `margin`    | No       | Locks all margins with a number or selected sides with a partial object. Omitted sides are measured automatically. |
| `theme`     | No       | Overrides default foreground, muted, grid, background, or palette tokens.                                          |

The detailed option contracts live in
[Scales, guides, and color](./scales-guides-and-color.md). Mark-specific
channels and defaults live in the [mark reference](./index.md#mark-reference).

## Marks and layer order

`marks` is the grammar's composition unit:

```ts
import { areaY, defineChart, lineY, ruleY } from '@tanstack/charts'
import { scaleLinear, scaleUtc } from 'd3-scale'

const definition = defineChart({
  marks: [
    areaY(rows, { x: 'date', y: 'value', fillOpacity: 0.12 }),
    ruleY([target], {
      stroke: '#dc2626',
      strokeWidth: 1.5,
      strokeDasharray: '4 2',
    }),
    lineY(rows, { x: 'date', y: 'value', points: true }),
  ],
  x: { scale: xScale },
  y: { scale: yScale, grid: true },
})
```

Each mark materializes channels for scale resolution, then emits renderer-
neutral scene nodes and optional interaction points. Marks may use different
datum types in the same spec. Their inferred datum types become a union in
interaction callbacks.

Use stable `key` channels for data that reorders, enters, or exits. Mark IDs
default from layer order; set `id` explicitly when a mark must retain identity
while its order changes.

## Required positional axes

`x` and `y` are intentionally required. Supply a D3 factory for an inferred
domain or a configured instance for a fixed domain:

```ts
const axes = {
  x: { scale: scaleUtc },
  y: { scale: scaleLinear },
}
```

Use `null` only when that dimension has no materialized channel:

```ts
const horizontalThresholds = defineChart({
  marks: [ruleY([25, 50, 75])],
  x: null,
  y: { scale: scaleLinear().domain([0, 100]) },
})
```

`guide: false` hides an axis but does not remove its scale. `x: null` or
`y: null` declares that no scale exists. Scene compilation throws when a mark
materializes values for a missing positional scale.

## Guides and margins

Guide visibility and geometry are separate:

- `x.guide: false` or `y.guide: false` hides one guide.
- `guides: false` hides all guides and removes their implicit margin.
- Omitted `margin` sides are measured from ticks, rotation, titles, and edge
  overhang.
- `margin: 0` locks every side to zero.
- `margin: { left: 80 }` locks only the left side.

Automatic margins contain labels; they do not choose a collision policy.
Control dense labels with the scale's tick behavior, `ticks`, `format`, or
`tickRotate`.

## Clip and gradient resources

`clip` and `gradients` are scene data. Render them with
`renderChartSvgWithResources`:

```ts
import { renderChartSvgWithResources } from '@tanstack/charts/svg/resources'

const definition = defineChart({
  marks,
  x,
  y,
  clip: true,
  gradients: [
    {
      id: 'revenue',
      y1: 1,
      y2: 0,
      stops: [
        { offset: 0, color: '#2563eb', opacity: 0.08 },
        { offset: 1, color: '#2563eb', opacity: 0.72 },
      ],
    },
  ],
})
```

Reference a declared gradient from a mark paint as `url(#revenue)` and select
the resource-aware renderer on the host or adapter. `idPrefix` scopes generated
resource IDs when multiple charts share a document. See
[Rendering and export](./rendering-and-export.md).

## Theme

The complete default theme is exported as `defaultChartTheme`:

```ts
interface ChartTheme {
  foreground: string
  muted: string
  grid: string
  background: string
  palette: readonly string[]
}
```

`theme` is partial. The palette is replaced as one value rather than merged by
index. The default palette uses CSS custom-property fallbacks:

```css
.dashboard {
  --ts-chart-1: #38bdf8;
  --ts-chart-2: #fb7185;
  --ts-chart-3: #4ade80;
}
```

Because the default foreground and guide colors use `currentColor`, charts
inherit light and dark mode without a JavaScript theme switch. Override theme
tokens when the application needs an explicit visual system.
