# chartist/server (Chartist API)

> Charts as markup without a DOM: SVG strings, markup the browser takes over, and stylesheets written into the SVG for images and PDFs.

## computeStyles

*experimental · from `chartist/server`*

```ts
function computeStyles(scene: SceneNode, css: string | readonly string[], options?: ComputeStylesOptions): ComputedStyles
```

Computes the styles a scene's SVG elements get from CSS, with Chartist's own CSS engine: a small one, made for chart styles. Pass the result to [`applyStyles`](https://chartist.dev/api/server/#applyStyles).

It matches rules as a browser does, per element, with the scene below a page's `html` and `body` (so `:root`, `html` and `body` rules apply, and their custom properties reach the chart): type, class and attribute selectors, `:root`, `:not()`, `:is()` and `:where()`, and the descendant and child combinators, ranked by `!important`, the element's own `style`, specificity and order. Custom properties are resolved per element, and `light-dark()` by `colorScheme`. Rules in `@media (prefers-color-scheme)` apply for their scheme, and `@supports` and `@layer` blocks are read in place.

What it doesn't support it leaves out, and reports to `onSkip`: other at-rules, sibling and structural selectors (`+`, `~`, `:nth-child()`), ids and other pseudo-classes, nested rules and the `font` shorthand. `calc()` and relative units such as `rem` are passed on as written. For full CSS, compute the styles with a browser and pass those to `applyStyles()`. Only the values of properties SVG has presentation attributes for are computed; elements inside a `foreignObject` (HTML labels) get none.

Pass only stylesheets your application controls: CSS values, `url()` included, end up in the markup.

 New with 2.1: the server API may still change in a 2.x   minor before it becomes stable.

Parameters:

- `scene: SceneNode` A scene, usually from `toScene()`
- `css: string | readonly string[]` A stylesheet's text, or several, applied in order as if the   later ones came later in the page
- `options?: ComputeStylesOptions` The color scheme (`light` by default), and `onSkip`

Returns: The values each node gets

```ts
import { lineChartConfig } from 'chartist';
import { applyStyles, computeStyles, toScene } from 'chartist/server';

const css = '.ct-line { stroke: teal; } .ct-series:nth-child(2) { stroke: red; }';
const scene = toScene(lineChartConfig({ series: [[5, 2, 4]] }), {
  width: 600,
  height: 300
});

const styles = computeStyles(scene, css, {
  // `.ct-series:nth-child(2)`: selector
  onSkip: ({ text, reason }) => console.warn(`${text}: ${reason}`)
});
export const styled = applyStyles(scene, styles);
```

## applyStyles

*experimental · from `chartist/server`*

```ts
function applyStyles(scene: SceneNode, styles: string | readonly string[] | ComputedStyles, options?: ComputeStylesOptions): SceneNode
```

Writes styles into a scene as SVG presentation attributes, so its markup draws the same in tools that read SVG on its own: rasterizers that make PNGs, PDF libraries, email. Every SVG element gets its values as attributes such as `stroke="#d70206"`; values of properties SVG has no attribute for are left out. Its classes stay, so a page can still style the markup, and elements inside a `foreignObject` (HTML labels) are left as they are: use SVG labels for images.

Give it CSS, which [`computeStyles`](https://chartist.dev/api/server/#computeStyles) computes with Chartist's own, small CSS engine, or the styles another engine computed, such as a headless browser's `getComputedStyle()` for each element.

 New with 2.1: the server API may still change in a 2.x   minor before it becomes stable.

Parameters:

- `scene: SceneNode` A scene, usually from `toScene()`; it isn't changed
- `styles: string | readonly string[] | ComputedStyles` The values per node from [`computeStyles`](https://chartist.dev/api/server/#computeStyles) or another   engine, or the CSS for `computeStyles()`: a stylesheet's text or   several, applied in order
- `options?: ComputeStylesOptions` With CSS: the options of `computeStyles()`

Returns: A copy of the scene with the styles as attributes

```ts
import { readFileSync } from 'node:fs';
import { createRequire } from 'node:module';
import { lineChartConfig } from 'chartist';
import { applyStyles, renderToString, toScene } from 'chartist/server';

const css = readFileSync(
  createRequire(import.meta.url).resolve('chartist/styles.css'),
  'utf8'
);
const scene = toScene(
  lineChartConfig({ labels: ['Mon', 'Tue', 'Wed'], series: [[5, 2, 4]] }),
  { width: 600, height: 300 }
);

// SVG for a rasterizer: the colors and widths as attributes.
export const svg = renderToString(applyStyles(scene, css));
```

## toScene

*from `chartist/server`*

Builds a chart's scene without rendering it: no DOM, timers or globals. The same as [`toScene`](https://chartist.dev/api/rendering/#toScene) from `chartist`, re-exported so server code needs one import.

## renderForHydration

*experimental · from `chartist/server`*

```ts
function renderForHydration(config: ChartConfig, size: SceneSize): string
```

Renders a chart to markup that `chart()` and the presets take over in the browser (hydration), instead of drawing a new chart. Needs no DOM, so it works in Node.js and in server bundles; put the markup into the chart's container once, and create the chart there with `liveDomRenderer()` from `chartist/live` as its renderer, which keeps the server's elements.

It lays the chart out as [`toScene`](https://chartist.dev/api/rendering/#toScene) does, at the given size. The root gets the size `chart()` gives it (`100%` unless the config sets one) and, unless the config sets a `viewBox`, a `viewBox` of the layout size, so the drawing scales into its container until the browser lays it out at the real size. Its `data-ct-hydrate` attribute records what the browser needs to build the same scene again: the size, the label mode, whether text was estimated and whether the `viewBox` only scales. Labels are HTML unless `size.labels` or the config says otherwise, as `chart()` draws them.

The markup has no `style` attributes, so it passes a Content Security Policy of `style-src 'self'`, and it needs Chartist's stylesheet in the page. Styles that SVG has attributes for (a label's `overflow`, a donut's `stroke-width`) are written as those; the others, such as styles a `scene` listener adds, are applied when the browser takes the markup over. For a self-contained SVG image, with its styles inline (files, email, a static chart), use `toScene()` and [`renderToString`](https://chartist.dev/api/server/#renderToString) instead.

 New with 2.1: the server API may still change in a 2.x   minor before it becomes stable.

Parameters:

- `config: ChartConfig` The chart, as the browser creates it: data, axes, layers,   plugins and listeners
- `size: SceneSize` The size to lay out at, in px, and how to measure text; use   the estimate (no `measureText`), which the browser replays

Returns: The SVG markup, its root marked with `data-ct-hydrate`

```ts
import { lineChartConfig } from 'chartist';
import { renderForHydration } from 'chartist/server';

const data = {
  labels: ['Mon', 'Tue', 'Wed', 'Thu', 'Fri'],
  series: [[12, 9, 7, 8, 5]]
};

// On the server, in the chart's container:
export const markup = renderForHydration(lineChartConfig(data), {
  width: 600,
  height: 300
});

// In the browser, the chart keeps the server's elements:
// lineChart('#chart', data, { renderer: liveDomRenderer() });
```

## renderToString

*experimental · from `chartist/server`*

```ts
function renderToString(scene: SceneNode): string
```

Serializes a scene to standalone SVG markup. Needs no DOM, so it works in Node.js and in server bundles.

The root gets the SVG and `ct:` namespaces, and attribute values and text are escaped. The markup has the chart's classes but no styles: load `chartist/styles.css` where it's shown, or add a `style` element to the scene.

 New in `chartist/server` with 2.1: the server API may still   change in a 2.x minor before it becomes stable.

Parameters:

- `scene: SceneNode` A scene, usually from `toScene()`

Returns: The SVG markup, e.g. `<svg xmlns="…" …>…</svg>`

```ts
import { lineChartConfig } from 'chartist';
import { renderToString, toScene } from 'chartist/server';

export function weekChart(values: number[]): string {
  const scene = toScene(
    lineChartConfig({
      labels: ['Mon', 'Tue', 'Wed', 'Thu', 'Fri'],
      series: [values]
    }),
    { width: 600, height: 300 }
  );
  return renderToString(scene);
}
```

## ComputedStyles

*experimental · from `chartist/server`*

```ts
type ComputedStyles = (node: SceneNode) => Readonly<Record<string, string>> | undefined
```

The styles of a scene's elements: for a scene node, the values of the CSS properties it gets (property to value, e.g. `{ stroke: '#d70206' }`), or nothing. [`computeStyles`](https://chartist.dev/api/server/#computeStyles) makes one with Chartist's own CSS engine; another engine can make one too, for [`applyStyles`](https://chartist.dev/api/server/#applyStyles).

 New with 2.1: the server API may still change in a 2.x   minor before it becomes stable.

## SkippedStyle

*experimental · from `chartist/server`*

```ts
interface SkippedStyle
```

A part of a stylesheet that [`computeStyles`](https://chartist.dev/api/server/#computeStyles) left out, as `onSkip` gets it: its text and why. `at-rule` for an at-rule other than `@media (prefers-color-scheme)`, `@supports` and `@layer`; `selector` for a selector it can't match; `nested` for a nested rule; `shorthand` for `font`; `value` for a declaration whose value doesn't resolve on an element (a `var()` without a value or fallback there).

 New with 2.1: the server API may still change in a 2.x   minor before it becomes stable.

Properties:

- `text: string` The part as written: an at-rule's prelude, a selector, a declaration.
- `reason: SkipReason` Why it was left out.

## ComputeStylesOptions

*experimental · from `chartist/server`*

```ts
interface ComputeStylesOptions
```

Options of [`computeStyles`](https://chartist.dev/api/server/#computeStyles).

 New with 2.1: the server API may still change in a 2.x   minor before it becomes stable.

Properties:

- `colorScheme?: 'light' | 'dark'` The color scheme to style for: which value of `light-dark()` and which `@media (prefers-color-scheme)` rules apply.
- `onSkip?: (skipped: SkippedStyle) => void` Called once for each part of the stylesheets that was left out, so a rule that doesn't apply says why. Rules for the other color scheme and pseudo-elements (`::before`), which style nothing in SVG, aren't reported.

## ApplyStylesOptions

*experimental · from `chartist/server`*

```ts
type ApplyStylesOptions = ComputeStylesOptions
```

Options of [`applyStyles`](https://chartist.dev/api/server/#applyStyles) when it gets CSS: those of [`computeStyles`](https://chartist.dev/api/server/#computeStyles).

 New with 2.1: the server API may still change in a 2.x   minor before it becomes stable.

Properties:

- `colorScheme?: 'light' | 'dark'` The color scheme to style for: which value of `light-dark()` and which `@media (prefers-color-scheme)` rules apply.
- `onSkip?: (skipped: SkippedStyle) => void` Called once for each part of the stylesheets that was left out, so a rule that doesn't apply says why. Rules for the other color scheme and pseudo-elements (`::before`), which style nothing in SVG, aren't reported.

## SkipReason

*experimental · from `chartist/server`*

```ts
type SkipReason = 'at-rule' | 'selector' | 'nested' | 'shorthand' | 'value'
```

Why `computeStyles()` left a part of a stylesheet out: `at-rule`, `selector`, `nested`, `shorthand` or `value` (see `SkippedStyle`).

 New with 2.1: the server API may still change in a 2.x   minor before it becomes stable.

## SceneSize

*from `chartist/server`*

The size toScene and `renderForHydration()` lay a chart out at, since there's no container to measure, and how to measure text there. The same as [`SceneSize`](https://chartist.dev/api/rendering/#SceneSize) from `chartist`, re-exported so server code needs one import.
