@vielzeug/codex 26.10.4 → 26.10.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/data/search.json CHANGED
@@ -936,16 +936,16 @@
936
936
  },
937
937
  {
938
938
  "category": "ui",
939
- "description": "responsive svg charts with explicit updates, keyboard and screen reader support, and css theming.",
939
+ "description": "responsive svg charts with explicit updates: line, bar, area, scatter, pie, radar, sparkline",
940
940
  "docs": {
941
- "index": " \ntitle: 'prism: responsive svg data visualization'\ndescription: responsive svg charts with explicit updates, keyboard and screen reader support, and css theming.\npackage: prism\ncategory: ui\nkeywords: [chart, svg, visualization, responsive, line chart, bar chart, area chart, pie chart, radar chart, sparkline, accessibility, typescript]\nrelated: [refine, orbit]\nexports:\n [\n createlinechart,\n createbarchart,\n createareachart,\n createpiechart,\n createradarchart,\n createsparkline,\n settheme,\n prismerror,\n ]\nenvironments: [browser]\n \n\n<! markdownlint disable md025 md033 md060 >\n\n<packagehero package=\"prism\" />\n\n## why prism?\n\ncharting libraries typically require a framework binding, bundle heavy dependencies, or force canvas rendering that can't be styled with css. prism takes a different approach:\n\n```ts\n// before: chart.js, imperative setup with a canvas you can't css theme\nimport chart from 'chart.js/auto';\nconst ctx = document.getelementbyid('mychart') as htmlcanvaselement;\nnew chart(ctx, {\n type: 'line',\n data: { labels, datasets: [{ data: values }] },\n // re create or mutate the chart.js instance when data changes\n});\n\n// after: prism, responsive svg with an explicit update boundary\nimport { createlinechart } from '@vielzeug/prism';\n\nconst chart = createlinechart(document.getelementbyid('chart')!, {\n a11y: { arialabel: 'users by day' },\n series: [\n {\n data: [\n { key: 1, value: 12 },\n { key: 2, value: 40 },\n { key: 3, value: 28 },\n ],\n name: 'users',\n },\n ],\n tooltip: true,\n});\n\nchart.update([\n {\n data: [\n { key: 1, value: 12 },\n { key: 2, value: 40 },\n { key: 3, value: 28 },\n { key: 4, value: 65 },\n ],\n name: 'users',\n },\n]);\n```\n\n| feature | prism | chart.js | lightweight charts | d3 |\n| | | | | |\n| bundle size | <packageinfo package=\"prism\" type=\"size\" /> | ~60 kb | ~45 kb | ~30 kb (core) |\n| zero dependencies | <ore icon name=\"x\" size=\"16\"></ore icon> (orbit) | <ore icon name=\"x\" size=\"16\"></ore icon> | <ore icon name=\"x\" size=\"16\"></ore icon> | <ore icon name=\"triangle alert\" size=\"16\"></ore icon> (modular) |\n| renderer | svg | canvas | canvas | svg/canvas |\n| data updates | explicit `update()` | instance mutation | series mutation | manual |\n| css themeable | <ore icon name=\"check\" size=\"16\"></ore icon> | <ore icon name=\"x\" size=\"16\"></ore icon> | limited | <ore icon name=\"check\" size=\"16\"></ore icon> |\n| framework neutral | <ore icon name=\"check\" size=\"16\"></ore icon> | <ore icon name=\"check\" size=\"16\"></ore icon> | <ore icon name=\"check\" size=\"16\"></ore icon> | <ore icon name=\"check\" size=\"16\"></ore icon> |\n| accessible svg | <ore icon name=\"check\" size=\"16\"></ore icon> | <ore icon name=\"x\" size=\"16\"></ore icon> | <ore icon name=\"x\" size=\"16\"></ore icon> | manual |\n| keyboard navigation | <ore icon name=\"check\" size=\"16\"></ore icon> | <ore icon name=\"x\" size=\"16\"></ore icon> | <ore icon name=\"x\" size=\"16\"></ore icon> | manual |\n| typescript first | <ore icon name=\"check\" size=\"16\"></ore icon> | partial | <ore icon name=\"check\" size=\"16\"></ore icon> | types available |\n\n<div class=\"decision callout\">\n\n**use prism when** you need lightweight, framework neutral charts with explicit updates and svg output that can be styled with css. it fits dashboards, admin panels, and data heavy applications.\n\n**consider alternatives when** you need 50+ chart types (echarts), financial trading charts (lightweight charts), or low level visualization grammar (d3).\n\n</div>\n\n## installation\n\n::: code group\n\n```sh [pnpm]\npnpm add @vielzeug/prism\n```\n\n```sh [npm]\nnpm install @vielzeug/prism\n```\n\n```sh [yarn]\nyarn add @vielzeug/prism\n```\n\n:::\n\n## quick start\n\n```ts\nimport { createlinechart } from '@vielzeug/prism';\nimport '@vielzeug/prism/theme.css';\n\nconst chart = createlinechart(document.getelementbyid('chart')!, {\n a11y: { arialabel: 'revenue by month' },\n series: [\n {\n color: '#3b82f6',\n data: [\n { key: 1, value: 10 },\n { key: 2, value: 25 },\n { key: 3, value: 18 },\n { key: 4, value: 32 },\n ],\n name: 'revenue',\n },\n ],\n xaxis: { position: 'bottom' },\n yaxis: { position: 'left', grid: true },\n tooltip: true,\n crosshair: true,\n onhover: (event) => console.log(event?.datum),\n});\n\n// update chart data explicitly\nchart.update([\n {\n color: '#3b82f6',\n data: [{ key: 1, value: 10 }, { key: 2, value: 25 }, { key: 3, value: 18 }, { key: 4, value: 32 }, { key: 5, value: 28 }],\n name: 'revenue',\n },\n]);\n\n// cleanup when done\nchart.dispose();\n```\n\n## features\n\n<div class=\"features grid\">\n\n **`createlinechart(container, config)`**: line chart with linear, monotone, or step interpolation\n **`createbarchart(container, config)`**: bar chart with four layout variants: grouped, stacked, grouped horizontal, stacked horizontal\n **`createareachart(container, config)`**: filled area with a gradient or solid fill\n **`createpiechart(container, config)`**: pie, donut, or semi circle donut chart; labels that do not fit their slice are hidden\n **`createradarchart(container, config)`**: radar chart for comparing values across 3+ axes, with per axis ranges\n **`createsparkline(container, config)`**: minimal inline line, area, bar, or stack chart\n **`linearscale`, `timescale`, `bandscale`**: standalone scales with nice tick generation\n **`charthandle.update(data)`**: replace chart data synchronously without coupling to a state library\n **`settheme(theme, { scope })` / `resettheme({ scope })`**: apply or clear colors, font, grid, axis, text, and tooltip tokens globally or on one subtree at runtime\n **axes on by default**: line, area, and bar charts render both axes with gridlines on the value axis; `xaxis: false` / `yaxis: false` opt out\n **animations on by default**: every chart animates entry and updates through one raf loop, honors reduced motion, and opts out with `transition: false`\n **series comparison**: hover or keyboard focus reports every series at the active key in the tooltip, `onhover`, and screen reader announcements\n **keyboard navigation**: arrow keys, `enter`, and `escape` on every labelled chart except sparklines\n **css custom properties**: full theme control via ` prism *` tokens, with dark mode built in\n **responsive**: auto resizes via `resizeobserver`\n **`charthandle.tap()`**: observe resize and dispose events outside the render path, with zero cost when untapped\n **`symbol.dispose`**: explicit resource management following the tc39 proposal\n\n</div>\n\n## documentation\n\n<div class=\"doc links\">\n\n [usage guide](./usage.md)\n [api reference](./api.md)\n [examples](./examples.md)\n [migration guide](./migration.md)\n\n</div>\n\n## see also\n\n<div class=\"see also\">\n\n [refine](/refine/): accessible web components that pair well with prism for dashboards\n [orbit](/orbit/): floating element positioning for chart tooltips and popovers\n\n</div>\n\n<! markdownlint enable md025 md033 md060 >\n",
942
- "api": " \ntitle: 'prism: api reference'\ndescription: complete chart, scale, theme, handle, configuration, and error contracts for @vielzeug/prism.\n \n\n[[toc]]\n\n## api overview\n\n| symbol | purpose | execution mode | common gotcha |\n| | | | |\n| `createlinechart()` | render a line chart | sync | line keys must be numbers or dates |\n| `createareachart()` | render an area chart | sync | line keys must be numbers or dates |\n| `createbarchart()` | render grouped or stacked bars | sync | stacked negative values are clamped to zero |\n| `createpiechart()` | render pie, donut, or semi circle slices | sync | `onhover` receives `null` when the pointer leaves |\n| `createradarchart()` | compare values across 3+ axes, such as hero stats | sync | events report a whole axis, not one series |\n| `createsparkline()` | render an inline line, area, bar, or stack | sync | omitted `a11y` makes the chart decorative |\n| `linearscale()` | create a numeric scale | sync | `nice` defaults to `true` |\n| `timescale()` | create a date scale | sync | invalid dates produce an invalid domain |\n| `bandscale()` | create a categorical scale | sync | unknown categories map to `0` and warn in development |\n| `settheme()` | set prism css custom properties | sync | writes to `document.documentelement` unless `scope` is given |\n| `resettheme()` | remove prism theme overrides | sync | clears only properties `settheme()` wrote, on the same target |\n| `seriescolor()` | resolve a series palette color | sync | palette indexes wrap after eight colors |\n\n## package entry point\n\n| import | purpose |\n| | |\n| `@vielzeug/prism` | chart factories, scales, theme helpers, errors, and public types |\n| `@vielzeug/prism/theme.css` | default css custom properties and dark mode values |\n\n## chart factories\n\n### `createlinechart()`\n\n```ts\nfunction createlinechart(\n container: htmlelement,\n config: linechartconfig,\n): charthandle<lineseriesconfig[]>;\n```\n\nrenders a line chart and returns a handle that replaces the complete series array through `update()`.\n\n| parameter | type | description |\n| | | |\n| `container` | `htmlelement` | host that receives the svg and optional overlays |\n| `config` | `linechartconfig` | initial series, axes, interaction, accessibility, and transition settings |\n\n**returns:** `charthandle<lineseriesconfig[]>`.\n\n```ts\nimport { createlinechart } from '@vielzeug/prism';\n\nconst chart = createlinechart(container, {\n a11y: { arialabel: 'revenue by month' },\n series: [{ data: [{ key: 1, value: 10 }], name: 'revenue' }],\n});\n\nchart.update([{ data: [{ key: 2, value: 20 }], name: 'revenue' }]);\n```\n\n \n\n### `createareachart()`\n\n```ts\nfunction createareachart(\n container: htmlelement,\n config: areachartconfig,\n): charthandle<areaseriesconfig[]>;\n```\n\nrenders an area chart and returns a handle that replaces the complete series array through `update()`.\n\n| parameter | type | description |\n| | | |\n| `container` | `htmlelement` | host that receives the svg and optional overlays |\n| `config` | `areachartconfig` | initial series, axes, interaction, accessibility, and transition settings |\n\n**returns:** `charthandle<areaseriesconfig[]>`.\n\n```ts\nimport { createareachart } from '@vielzeug/prism';\n\nconst chart = createareachart(container, {\n series: [{ data: [{ key: 1, value: 10 }], name: 'revenue' }],\n});\n```\n\n \n\n### `createbarchart()`\n\n```ts\nfunction createbarchart(\n container: htmlelement,\n config: barchartconfig,\n): charthandle<barseriesconfig[]>;\n```\n\nrenders grouped, stacked, grouped horizontal, or stacked horizontal bars.\n\n| parameter | type | description |\n| | | |\n| `container` | `htmlelement` | host that receives the svg and optional overlays |\n| `config` | `barchartconfig` | initial series, variant, axes, accessibility, and interaction settings |\n\n**returns:** `charthandle<barseriesconfig[]>`.\n\n```ts\nimport { createbarchart } from '@vielzeug/prism';\n\nconst chart = createbarchart(container, {\n series: [{ data: [{ key: 'open', value: 12 }], name: 'tasks' }],\n variant: 'grouped',\n});\n```\n\n \n\n### `createpiechart()`\n\n```ts\nfunction createpiechart(\n container: htmlelement,\n config: piechartconfig,\n): charthandle<piesliceconfig[]>;\n```\n\nrenders pie, donut, or semi circle slices.\n\n| parameter | type | description |\n| | | |\n| `container` | `htmlelement` | host that receives the svg and optional overlays |\n| `config` | `piechartconfig` | initial slices, variant, geometry, accessibility, and interaction settings |\n\n**returns:** `charthandle<piesliceconfig[]>`.\n\n```ts\nimport { createpiechart } from '@vielzeug/prism';\n\nconst chart = createpiechart(container, {\n data: [{ label: 'complete', value: 72 }, { label: 'remaining', value: 28 }],\n variant: 'donut',\n});\n```\n\n \n\n### `createradarchart()`\n\n```ts\nfunction createradarchart(\n container: htmlelement,\n config: radarchartconfig,\n): charthandle<radarseriesconfig[]>;\n```\n\nrenders one closed shape per series across three or more axes. `update()` replaces the series; the axes are fixed at creation.\n\n| parameter | type | description |\n| | | |\n| `container` | `htmlelement` | host that receives the svg and optional overlays |\n| `config` | `radarchartconfig` | axes, series, scale, grid, appearance, accessibility, and interaction settings |\n\n| field | type | default | description |\n| | | | |\n| `axes` | `radaraxisconfig[]` | n/a | ordered axes, drawn clockwise; needs at least 3 |\n| `series` | `radarseriesconfig[]` | n/a | values keyed by `axis.key` |\n| `domain` | `[number, number]` | `[0, nice max]` | shared value range; `axis.min`/`axis.max` override it per axis |\n| `grid` | `boolean \\| radargridconfig` | `true` | rings, bands, and level labels; `false` hides the grid |\n| `fill` | `'gradient' \\| 'none' \\| 'solid'` | `'solid'` | shape fill; `gradient` deepens toward the rim |\n| `curve` | `'linear' \\| 'rounded'` | `'linear'` | straight edges or a smooth closed curve |\n| `showpoints` | `boolean` | `true` | vertex dots; a missing value shows a hollow dot at the centre |\n| `showvalues` | `boolean` | `false` | print each value beside its vertex |\n| `startangle` | `number` | `0` | degrees clockwise from 12 o'clock for the first axis |\n| `onhover` | `(event: radarevent \\| null) => void` | n/a | nearest axis under the pointer or keyboard focus |\n| `onclick` | `(event: radarevent) => void` | n/a | axis clicked, or activated with enter/space |\n\n**returns:** `charthandle<radarseriesconfig[]>`.\n\n```ts\nimport { createradarchart } from '@vielzeug/prism';\n\nconst chart = createradarchart(container, {\n a11y: { arialabel: 'hero stats' },\n axes: [\n { key: 'str', label: 'strength' },\n { key: 'agi', label: 'agility' },\n { key: 'end', label: 'endurance' },\n ],\n domain: [0, 10],\n series: [{ data: [{ key: 'str', value: 8 }, { key: 'agi', value: 6 }, { key: 'end', value: 9 }], name: 'adam' }],\n});\n```\n\nhovering or arrowing onto an axis highlights its spoke and reports every series' value on it. without a tooltip, the chart announces the values through its own `role=\"status\"` region. a value for an unknown axis, duplicate axis keys, or fewer than 3 axes warns in development.\n\n \n\n### `createsparkline()`\n\n```ts\nfunction createsparkline(\n container: htmlelement,\n config: sparklineconfig,\n): charthandle<number[] | stacksegment[]>;\n```\n\nrenders a compact chart without axes, margin, or legend.\n\n| parameter | type | description |\n| | | |\n| `container` | `htmlelement` | host that receives the svg |\n| `config` | `sparklineconfig` | initial values, variant, appearance, accessibility, and callbacks |\n\n**returns:** `charthandle<number[] | stacksegment[]>`.\n\n```ts\nimport { createsparkline } from '@vielzeug/prism';\n\nconst chart = createsparkline(container, { data: [2, 5, 3, 8], variant: 'area' });\nchart.update([2, 5, 3, 8, 13]);\n```\n\n## scale factories\n\n### `linearscale()`\n\n```ts\nfunction linearscale(config: linearscaleconfig): scale<number>;\n```\n\n| field | type | default | description |\n| | | | |\n| `domain` | `[number, number]` | n/a | input extent |\n| `range` | `[number, number]` | n/a | output extent |\n| `clamp` | `boolean` | `false` | clamp mapped and inverted values |\n| `nice` | `boolean` | `true` | expand the domain to rounded boundaries |\n\n**returns:** `scale<number>`.\n\n```ts\nimport { linearscale } from '@vielzeug/prism';\n\nconst scale = linearscale({ domain: [0, 100], range: [0, 500] });\nscale.map(50); // 250\n```\n\n \n\n### `timescale()`\n\n```ts\nfunction timescale(config: timescaleconfig): scale<date>;\n```\n\n| field | type | default | description |\n| | | | |\n| `domain` | `[date, date]` | n/a | input date extent |\n| `range` | `[number, number]` | n/a | output extent |\n| `nice` | `boolean` | `true` | expand the domain to rounded interval boundaries |\n\n**returns:** `scale<date>`.\n\n```ts\nimport { timescale } from '@vielzeug/prism';\n\nconst scale = timescale({\n domain: [new date('2026 01 01'), new date('2026 12 31')],\n range: [0, 500],\n});\n```\n\n \n\n### `bandscale()`\n\n```ts\nfunction bandscale(config: bandscaleconfig): bandscale;\n```\n\n| field | type | default | description |\n| | | | |\n| `domain` | `string[]` | n/a | ordered categories |\n| `range` | `[number, number]` | n/a | output extent |\n| `padding` | `number` | `0.1` | inner gap ratio |\n| `paddingouter` | `number` | `padding` | outer gap ratio |\n\n**returns:** `bandscale`.\n\n```ts\nimport { bandscale } from '@vielzeug/prism';\n\nconst scale = bandscale({ domain: ['a', 'b'], range: [0, 200] });\n```\n\n## theme utilities\n\n### `settheme()`\n\n```ts\nfunction settheme(theme: prismtheme, options?: themescope): void;\n```\n\nsets prism theme custom properties as inline styles on `options.scope` when given, otherwise on `document.documentelement`. inline values outrank every stylesheet rule, including dark mode selectors.\n\n| parameter | type | description |\n| | | |\n| `theme` | `prismtheme` | tokens to write; omitted keys are left untouched |\n| `options.scope` | `htmlelement` | subtree to theme instead of `:root`; charts inside it resolve their `var( prism *)` lookups against these values |\n\n### `resettheme()`\n\n```ts\nfunction resettheme(options?: themescope): void;\n```\n\nremoves every custom property `settheme()` can set from `options.scope` (default `documentelement`). scoped and global calls are independent targets.\n\n### `seriescolor()`\n\n```ts\nfunction seriescolor(index: number, override?: string): string;\n```\n\nreturns `override` when provided; otherwise returns `var( prism color n)` with an eight color wrap.\n\n## types\n\n### core types\n\n```ts\ninterface charthandle<tdata = unknown> {\n readonly disposalsignal: abortsignal;\n readonly disposed: boolean;\n readonly el: svgsvgelement;\n tap(handler: (event: prismevent) => void, options?: { readonly signal?: abortsignal }): () => void;\n update(data: tdata): void;\n dispose(): void;\n [symbol.dispose](): void;\n}\n\ntype prismevent =\n | { readonly height: number; readonly type: 'resize'; readonly width: number }\n | { readonly type: 'dispose' };\n\ninterface chartmargin {\n top: number;\n right: number;\n bottom: number;\n left: number;\n}\n\ninterface chartdimensions {\n width: number;\n height: number;\n margin: chartmargin;\n}\n\ntype charta11y =\n | { readonly decorative: true }\n | { readonly arialabel: string; readonly decorative?: false };\n```\n\n`update()` renders synchronously and throws `prismrendererror` after disposal. omitting `a11y` makes the svg decorative with `aria hidden=\"true\"`.\n\n`tap()` observes runtime behavior outside the render path: it receives `resize` and `dispose` events (see `prismevent`). it returns an unsubscribe function; pass `options.signal` to detach automatically. handler errors are swallowed, and tapping a disposed handle returns a no op unsubscribe.\n\n \n\n### data types\n\n```ts\ninterface datum<tkey extends date | number | string = date | number | string> {\n key: tkey;\n value: number;\n meta?: record<string, unknown>;\n dash?: string; // stroke dasharray for this datum's marks, e.g. '5 5'\n opacity?: number; // opacity multiplier 0 1 for this datum's marks, e.g. 0.4\n}\n\ntype continuousdatum = datum<date | number>;\n\ninterface series<tdatum extends datum = datum> {\n name: string;\n data: tdatum[];\n color?: string;\n id?: string; // stable data series id on the series group; defaults to series <index>\n}\n\ninterface chartevent {\n datum: datum;\n series: series;\n originalevent: event;\n values?: seriesvalue[]; // every series at the same key (line, area, bar)\n}\n\ninterface seriesvalue {\n datum: datum | undefined;\n series: series;\n}\n```\n\nline and area series use `continuousdatum`; bar series accept the complete `datum` key range. on line, area, and bar charts, `datum`/`series` name the series nearest the pointer, and `values` lists every series at that key (a missing value is `undefined`). `originalevent` is a `mouseevent`, or a `keyboardevent` for keyboard navigation.\n\n#### per datum presentation\n\n`dash` and `opacity` style an individual datum's marks without splitting the series — the usual case is a forecast tail where points past \"today\" are faded or dashed. they are styling only: interaction, tooltip, and accessibility behavior never change. support is per chart type:\n\n| chart | `dash` | `opacity` |\n| | | |\n| bar | `stroke dasharray` on the datum's `rect`, plus a 1px series color stroke so the dash is visible on the fill. | element `opacity` on the datum's `rect`. |\n| line | the segment from this datum to the next (chart.js style segments). the last datum's `dash` has no segment and is ignored. | that same segment, plus this datum's own point marker. |\n| area | the top line's segment from this datum to the next (last datum ignored). the fill keeps the series level treatment. | same segment on the top line. |\n| sparkline, pie, radar | ignored. | ignored. |\n\nconsecutive datums that share one `dash`/`opacity` render as a single path run. absent fields keep the series/theme default, and a series with no presentation fields renders byte identically to a chart that never used them. see [forecast / emphasis styling](./usage.md#forecast emphasis styling).\n\n#### stable series identity\n\n`series.id` is rendered as `data series id` on the chart's series group (`.prism line series`, `.prism bar series`, `.prism area series`, `.prism radar series`) so tooltips, tests, and external css can address a series without positional assumptions. characters outside `[a za z0 9_ ]` are replaced with ` `. it defaults to `series <index>`, which only stays stable while the series order does — set `id` to survive reordering.\n\n \n\n### chart configurations\n\n```ts\ninterface basechartconfig {\n a11y?: charta11y;\n legend?: boolean | legendconfig;\n margin?: partial<chartmargin>;\n onclick?: (event: chartevent) => void;\n onhover?: (event: chartevent | null) => void;\n tooltip?: boolean | tooltipconfig;\n transition?: transitionoption; // motion is on by default; false renders synchronously\n // omitted renders the axis with defaults (value axis with gridlines); false suppresses it.\n xaxis?: xaxisconfig | false;\n yaxis?: yaxisconfig | false;\n}\n\ninterface linechartconfig extends basechartconfig {\n crosshair?: boolean | crosshairconfig;\n rightyaxis?: omit<yaxisconfig, 'position' | 'grid'>;\n series: lineseriesconfig[];\n}\n\ninterface areachartconfig extends basechartconfig {\n crosshair?: boolean | crosshairconfig;\n series: areaseriesconfig[];\n}\n\ninterface barchartconfig extends basechartconfig {\n series: barseriesconfig[];\n variant?: barvariant;\n}\n\ninterface piechartconfig\n extends omit<basechartconfig, 'margin' | 'onclick' | 'onhover' | 'xaxis' | 'yaxis'> {\n data: piesliceconfig[];\n variant?: pievariant;\n innerradius?: number;\n cornerradius?: number;\n padpixels?: number;\n onclick?: (event: pieevent) => void;\n onhover?: (event: pieevent | null) => void;\n}\n\ninterface radarchartconfig\n extends omit<basechartconfig, 'margin' | 'onclick' | 'onhover' | 'xaxis' | 'yaxis'> {\n axes: radaraxisconfig[];\n series: radarseriesconfig[];\n curve?: 'linear' | 'rounded';\n domain?: [number, number];\n fill?: 'gradient' | 'none' | 'solid';\n grid?: boolean | radargridconfig;\n showpoints?: boolean;\n showvalues?: boolean;\n startangle?: number;\n onclick?: (event: radarevent) => void;\n onhover?: (event: radarevent | null) => void;\n}\n\ninterface sparklineconfig {\n data: number[] | stacksegment[];\n variant?: sparklinevariant;\n a11y?: charta11y;\n color?: string;\n cornerradius?: number;\n curve?: 'linear' | 'monotone' | 'step';\n fillopacity?: number; // overrides prism spark fill opacity (area variant)\n onclick?: (event: sparklineevent) => void;\n onhover?: (event: sparklineevent | null) => void;\n padpixels?: number;\n showendpoint?: boolean; // default true: dot on the latest value (line, area)\n strokewidth?: number;\n transition?: transitionoption; // motion is on by default; false renders synchronously\n}\n```\n\n \n\n### series and slice configurations\n\n```ts\ninterface lineseriesconfig extends series<continuousdatum> {\n yaxis?: 'left' | 'right'; // defaults to 'left'\n curve?: 'linear' | 'monotone' | 'step';\n pointradius?: number;\n showpoints?: boolean;\n strokewidth?: number; // overrides prism line width\n}\n\ninterface areaseriesconfig extends series<continuousdatum> {\n curve?: 'linear' | 'monotone' | 'step';\n fill?: 'gradient' | 'solid'; // default 'gradient', fading toward the baseline\n fillopacity?: number; // overrides prism area opacity\n showline?: boolean;\n}\n\ninterface barseriesconfig extends series {\n borderradius?: number; // overrides prism bar radius; stacked bars stay square\n}\n\ninterface piesliceconfig {\n value: number;\n color?: string;\n label?: string;\n}\n\ninterface pieevent {\n index: number;\n originalevent: event;\n slice: piesliceconfig;\n}\n\ninterface radaraxisconfig {\n key: string;\n label: string;\n min?: number;\n max?: number;\n format?: (value: number) => string;\n}\n\ntype radarseriesconfig = series<datum<string>>;\n\ninterface radargridconfig {\n bands?: boolean; // default true\n labels?: boolean; // default false; drawn only when every axis shares one domain\n levels?: number; // default 4\n shape?: 'circle' | 'polygon'; // default 'polygon'\n}\n\ninterface radarevent {\n axis: radaraxisconfig;\n index: number;\n values: radaraxisvalue[];\n originalevent: event;\n}\n\ninterface radaraxisvalue {\n datum: datum<string> | undefined;\n series: radarseriesconfig;\n}\n\ninterface stacksegment {\n value: number;\n color?: string;\n label?: string;\n}\n\ninterface sparklineevent {\n index: number;\n originalevent: event;\n value: number;\n}\n```\n\na custom radar `tooltip.render` receives `datum.meta` with `axis`, `values`, and the default `text`.\n\n \n\n### axis, interaction, and transition configurations\n\n```ts\ntype horizontalaxisposition = 'bottom' | 'top';\ntype verticalaxisposition = 'left' | 'right';\ntype axisposition = horizontalaxisposition | verticalaxisposition;\n\ninterface axisconfig<tposition extends axisposition = axisposition> {\n grid?: boolean | gridconfig;\n label?: string;\n position?: tposition;\n tickcount?: number;\n tickformat?: (value: date | number | string) => string;\n}\n\ntype xaxisconfig = axisconfig<horizontalaxisposition>;\ntype yaxisconfig = axisconfig<verticalaxisposition>;\n\ninterface gridconfig {\n color?: string;\n dash?: string;\n}\n\ninterface tooltipconfig {\n offset?: number;\n render?: (datum: datum, series: series) => node | string;\n}\n\ninterface crosshairconfig {\n horizontal?: boolean;\n snap?: boolean;\n vertical?: boolean;\n}\n\ninterface legendconfig {\n position?: legendposition;\n}\n\ntype legendposition = 'bottom' | 'left' | 'right' | 'top';\n\ninterface transitionconfig {\n duration?: number; // ms per lane; default 300\n easing?: easingname | easingfn;\n preference?: 'always' | 'never' | 'system'; // 'system' (default) skips reduced motion\n stagger?: number; // ms per bar (bar) or series (radar); ignored elsewhere\n}\n\n// motion is on by default. `false` renders every update synchronously,\n// `true` uses the chart defaults, or pass a config to tune them.\ntype transitionoption = transitionconfig | boolean;\n\ntype easingname = 'ease in' | 'ease in out' | 'ease out' | 'back out' | 'expo out' | 'linear';\n\ntype easingfn = (t: number) => number;\n```\n\ntooltip strings are assigned through `textcontent`. return a `node` for structured content.\n\n \n\n### scale types\n\n```ts\ninterface scale<t> {\n readonly domain: readonly [t, t];\n readonly range: readonly [number, number];\n map(value: t): number;\n invert(pixel: number): t;\n ticks(count?: number): t[];\n}\n\ninterface bandscale {\n readonly domain: readonly string[];\n readonly range: readonly [number, number];\n map(value: string): number;\n ticks(count?: number): string[];\n bandwidth(): number;\n gap(): number;\n}\n\ninterface linearscaleconfig {\n domain: [number, number];\n range: [number, number];\n clamp?: boolean;\n nice?: boolean;\n}\n\ninterface timescaleconfig {\n domain: [date, date];\n range: [number, number];\n nice?: boolean;\n}\n\ninterface bandscaleconfig {\n domain: string[];\n range: [number, number];\n padding?: number;\n paddingouter?: number;\n}\n```\n\n \n\n### remaining unions and theme type\n\n```ts\ntype barvariant = 'grouped' | 'grouped horizontal' | 'stacked' | 'stacked horizontal';\ntype pievariant = 'donut' | 'pie' | 'semi';\ntype sparklinevariant = 'area' | 'bar' | 'line' | 'stack';\n\ninterface prismtheme {\n axiscolor?: string; // prism axis color\n colors?: string[];\n fontfamily?: string;\n gridcolor?: string;\n gridopacity?: number;\n textcolor?: string; // prism text color\n textcolorsecondary?: string; // prism text color secondary\n tooltipbg?: string; // prism tooltip bg\n tooltipborder?: string; // prism tooltip border\n tooltipcolor?: string; // prism tooltip color\n}\n\ninterface themescope {\n readonly scope?: htmlelement;\n}\n```\n\n## errors\n\n### `prismerror`\n\nbase class for every prism originated error. it supports standard `erroroptions` cause chaining.\n\n### `prismrendererror`\n\nextends `prismerror`. thrown for invalid containers, failed initial renders, and attempts to update disposed charts.\n",
943
- "usage": " \ntitle: 'prism: usage guide'\ndescription: concepts, update patterns, and best practices for responsive svg charts with @vielzeug/prism.\n \n\n[[toc]]\n\n## basic usage\n\nevery chart needs a container element with defined dimensions and the theme css:\n\n```ts\nimport { createlinechart } from '@vielzeug/prism';\nimport '@vielzeug/prism/theme.css';\n\nconst container = document.queryselector<htmlelement>('#chart')!;\nconst chart = createlinechart(container, {\n series: [\n {\n name: 'revenue',\n data: [\n { key: 1, value: 10 },\n { key: 2, value: 16 },\n ],\n },\n ],\n});\n\nchart.dispose();\n```\n\n```html\n<div id=\"chart\" style=\"width: 100%; height: 300px;\"></div>\n```\n\nprism observes the container size via `resizeobserver` and re renders automatically on resize. if the container has zero dimensions at mount time, a `warn` is emitted in development: ensure the container has layout before calling the chart factory.\n\n## updating data\n\nchart factories render synchronously and return a typed handle. call `update()` with the same data shape used by the factory when application state changes.\n\n```ts\nimport { createlinechart } from '@vielzeug/prism';\n\nconst chart = createlinechart(container, {\n series: [{ data: [{ key: 1, value: 10 }], name: 'live' }],\n});\n\nchart.update([\n {\n data: [\n { key: 1, value: 10 },\n { key: 2, value: 20 },\n ],\n name: 'live',\n },\n]);\n```\n\n`update()` is state library neutral. call it from a framework effect, store subscription, event handler, or request callback. it throws after the chart is disposed instead of silently retaining detached state.\n\n## line charts\n\n```ts\nimport { createlinechart } from '@vielzeug/prism';\n\nconst chart = createlinechart(container, {\n series: [\n {\n name: 'revenue',\n data: [\n { key: 1, value: 100 },\n { key: 2, value: 150 },\n { key: 3, value: 130 },\n ],\n color: '#3b82f6',\n curve: 'monotone', // 'linear' | 'monotone' | 'step'\n strokewidth: 2,\n showpoints: true,\n pointradius: 4,\n },\n ],\n xaxis: { position: 'bottom' },\n yaxis: { position: 'left', grid: true },\n tooltip: true,\n crosshair: true,\n});\n```\n\n### multiple series\n\n```ts\nconst chart = createlinechart(container, {\n series: [\n { name: 'revenue', data: revenuedata, color: '#3b82f6' },\n { name: 'expenses', data: expensedata, color: '#ef4444' },\n ],\n xaxis: { position: 'bottom' },\n yaxis: { position: 'left', grid: true },\n});\n```\n\n### independent value axes\n\nassign a line series to `yaxis: 'right'` when its values need a different range. both axes share the same x scale, while tooltips retain the original values.\n\n```ts\nconst chart = createlinechart(container, {\n series: [\n { name: 'health', data: [{ key: 0, value: 30 }, { key: 1, value: 20 }] },\n { name: 'damage', yaxis: 'right', data: [{ key: 0, value: 0 }, { key: 1, value: 300 }] },\n ],\n yaxis: { label: 'health' },\n rightyaxis: { label: 'damage' },\n tooltip: true,\n});\n```\n\nthe primary axis is on the left when right axis series are present. grid configuration belongs to the primary axis. dual axis curve crossings do not imply equal numeric values. preserve each marker's axis assignment when calling `update()`.\n\n### time based x axis\n\nwhen data points use `date` objects for `key`, prism automatically applies a time scale. the domain spans exactly the first to the last date, so the series fills the plot width:\n\n```ts\nconst chart = createlinechart(container, {\n series: [\n {\n name: 'signups',\n data: [\n { key: new date('2024 01 01'), value: 50 },\n { key: new date('2024 02 01'), value: 80 },\n { key: new date('2024 03 01'), value: 120 },\n ],\n },\n ],\n xaxis: { position: 'bottom', tickformat: (d) => (d as date).tolocaledatestring() },\n yaxis: { position: 'left' },\n});\n```\n\n## bar charts\n\n```ts\nimport { createbarchart } from '@vielzeug/prism';\n\nconst chart = createbarchart(container, {\n series: [\n {\n name: 'sales',\n data: [\n { key: 'q1', value: 200 },\n { key: 'q2', value: 350 },\n { key: 'q3', value: 280 },\n { key: 'q4', value: 400 },\n ],\n borderradius: 4,\n },\n ],\n xaxis: { position: 'bottom' },\n yaxis: { position: 'left', grid: true },\n tooltip: true,\n});\n```\n\n### variants\n\nselect the bar layout with `variant`:\n\n| value | layout |\n| | |\n| `'grouped'` | vertical grouped (default) |\n| `'stacked'` | vertical stacked |\n| `'grouped horizontal'` | horizontal grouped |\n| `'stacked horizontal'` | horizontal stacked |\n\n```ts\nconst chart = createbarchart(container, {\n variant: 'stacked',\n series: [\n { name: 'mobile', data: mobiledata, color: '#3b82f6', borderradius: 0 },\n { name: 'desktop', data: desktopdata, color: '#10b981', borderradius: 0 },\n ],\n xaxis: { position: 'bottom' },\n yaxis: { position: 'left', grid: true },\n tooltip: true,\n legend: true,\n});\n```\n\nfor horizontal layouts, categories appear on the y axis and values on the x axis:\n\n```ts\nconst chart = createbarchart(container, {\n variant: 'grouped horizontal',\n series: [{ name: 'revenue', data, color: '#3b82f6' }],\n xaxis: { position: 'bottom', grid: true },\n yaxis: { position: 'left' },\n});\n```\n\n### grouped bars\n\nmultiple series with `variant: 'grouped'` (default) render side by side:\n\n```ts\nconst chart = createbarchart(container, {\n series: [\n { name: '2023', data: lastyeardata, color: '#94a3b8' },\n { name: '2024', data: thisyeardata, color: '#3b82f6' },\n ],\n});\n```\n\n## area charts\n\narea fills use a vertical gradient that fades toward the baseline. set `fill: 'solid'` for a flat fill:\n\n```ts\nimport { createareachart } from '@vielzeug/prism';\n\nconst chart = createareachart(container, {\n series: [\n {\n name: 'users',\n data: userdata,\n curve: 'monotone',\n fill: 'gradient', // 'gradient' (default) | 'solid'\n showline: true,\n },\n ],\n xaxis: { position: 'bottom' },\n yaxis: { position: 'left', grid: true },\n crosshair: true,\n});\n```\n\n`fillopacity` overrides the theme opacity for one series.\n\n## forecast / emphasis styling\n\na single series often mixes points that should look different — actuals plus a\nforecast tail, or a run that crossed a budget. rather than split the series,\ncarry `dash` and `opacity` on the individual datums. they are styling only:\ntooltips, hover, keyboard, and screen reader behavior never change.\n\nfor a forecast tail, map a parallel `types` array onto the datums:\n\n```ts\nimport { createlinechart } from '@vielzeug/prism';\n\nconst types = ['real', 'real', 'forecast', 'forecast'];\n\nconst chart = createlinechart(container, {\n series: [\n {\n name: 'cost',\n data: values.map((value, i) => ({\n key: months[i],\n value,\n ...(types[i] === 'forecast' ? { dash: '5 5', opacity: 0.4 } : {}),\n })),\n },\n ],\n});\n```\n\non a line chart, a datum's `dash` styles the segment from that point to the\nnext, so the forecast run renders dashed while the actuals stay solid; its\n`opacity` fades that segment and the point marker. the last datum's `dash` has\nno segment to paint and is ignored. on a bar chart, both fields apply to that\ndatum's own bar — a `dash` adds a 1px series color stroke so the dash reads on\nthe filled rect. on an area chart they style the top line only; the fill keeps\nits series level treatment.\n\nthe same mapping fades bars:\n\n```ts\nimport { createbarchart } from '@vielzeug/prism';\n\nconst chart = createbarchart(container, {\n series: [\n {\n name: 'cost',\n data: values.map((value, i) => ({\n key: months[i],\n value,\n ...(types[i] === 'forecast' ? { dash: '5 5', opacity: 0.4 } : {}),\n })),\n },\n ],\n});\n```\n\nbecause the fields live on the data, a normal `update(newseries)` re applies\nthem; removing the fields on the next update clears the styling. a series with\nno `dash`/`opacity` anywhere renders exactly as it did before these fields\nexisted.\n\n## pie, donut, and semi circle charts\n\nall three variants use `createpiechart` with the `variant` field:\n\n```ts\nimport { createpiechart } from '@vielzeug/prism';\n\nconst chart = createpiechart(container, {\n data: [\n { label: 'direct', value: 42, color: '#3b82f6' },\n { label: 'organic', value: 28, color: '#10b981' },\n { label: 'referral', value: 18, color: '#f59e0b' },\n { label: 'social', value: 12, color: '#8b5cf6' },\n ],\n variant: 'donut', // 'pie' | 'donut' | 'semi'\n tooltip: true,\n transition: { duration: 400, easing: 'ease out' },\n});\n```\n\n### variants\n\n| value | shape |\n| | |\n| `'pie'` | full circle, no hole |\n| `'donut'` | full circle with inner hole (~55% of outer by default) |\n| `'semi'` | top half semicircle with inner hole: useful for gauges |\n\n### inner radius\n\n`innerradius` overrides the automatic calculation:\n\n```ts\ncreatepiechart(container, {\n data,\n variant: 'donut',\n innerradius: 60, // explicit pixels\n});\n```\n\n### slice labels\n\nset `label` on each `piesliceconfig` to render text at the arc centroid. a label that does not fit inside its slice is not drawn; the value stays available through the tooltip, legend, and keyboard announcements:\n\n```ts\n{ value: 42, label: 'direct' }\n```\n\nstyle labels via css:\n\n```css\n:root {\n prism pie label color: #fff;\n prism pie label size: 11px;\n}\n```\n\n### updating data\n\n```ts\nconst chart = createpiechart(container, {\n data: [\n { label: 'a', value: 40 },\n { label: 'b', value: 60 },\n ],\n variant: 'donut',\n});\n\nchart.update([\n { label: 'a', value: 55 },\n { label: 'b', value: 45 },\n]);\n```\n\n### event hooks\n\n```ts\ncreatepiechart(container, {\n data,\n onhover: (event) => {\n // event is pieevent | null (null on mouseleave)\n if (event) console.log(event.index, event.slice.label, event.slice.value);\n },\n onclick: (event) => {\n console.log('clicked', event.index, event.slice.label);\n },\n});\n```\n\n`pieevent` provides `index`, the hovered/clicked `slice` (`piesliceconfig`), and the `originalevent` (`mouseevent` or `keyboardevent`).\n\n## radar charts\n\n`createradarchart` compares several values on one shape, such as a hero's stats. each series supplies values keyed by `axis.key`:\n\n```ts\nimport { createradarchart } from '@vielzeug/prism';\n\nconst axes = [\n { key: 'str', label: 'strength' },\n { key: 'agi', label: 'agility' },\n { key: 'int', label: 'intellect' },\n { key: 'end', label: 'endurance' },\n { key: 'spd', label: 'speed', max: 5 },\n];\n\nconst chart = createradarchart(container, {\n a11y: { arialabel: 'hero stats' },\n axes,\n domain: [0, 10],\n fill: 'gradient',\n showvalues: true,\n series: [\n {\n name: 'adam',\n data: [\n { key: 'str', value: 8 },\n { key: 'agi', value: 6 },\n { key: 'int', value: 4 },\n { key: 'end', value: 9 },\n { key: 'spd', value: 3 },\n ],\n },\n ],\n});\n```\n\n### scales\n\nevery axis shares `domain` (or a nice range around the data when omitted). give an axis its own `min`/`max` when its stat uses a different range: above, speed runs 0 to 5 while the rest run 0 to 10, so each still fills the chart.\n\n### appearance\n\n| option | values | use it for |\n| | | |\n| `fill` | `'solid'`, `'gradient'`, `'none'` | `gradient` for a single hero; `solid` or `none` when overlaying several |\n| `curve` | `'linear'`, `'rounded'` | rounded reads softer; linear keeps exact vertices |\n| `grid.shape` | `'polygon'`, `'circle'` | polygon matches the axes; circle suits many axes |\n| `grid.levels` / `grid.bands` | number / boolean | ring count and alternate shading |\n| `grid.labels` | boolean | level values; drawn only when every axis shares one domain |\n| `showvalues` | boolean | value beside each vertex, formatted by `axis.format` |\n\n### comparing series\n\npass several series with `legend: true`. hovering one shape dims the others, and hovering an axis reports every series' value on it:\n\n```ts\ncreateradarchart(container, {\n axes,\n legend: true,\n series: [adam, eve],\n tooltip: true, // \"agility: adam 6, eve 9\"\n onhover: (event) => event && console.log(event.axis.label, event.values),\n});\n```\n\n## sparklines\n\nsparklines are minimal inline charts with no axes, no legend, and no margin: designed to live inline with text or inside table cells.\n\n```ts\nimport { createsparkline } from '@vielzeug/prism';\n\nconst spark = createsparkline(container, {\n data: [12, 18, 14, 22, 19, 28],\n variant: 'line', // 'line' | 'area' | 'bar' | 'stack' (default: 'line')\n color: '#3b82f6',\n curve: 'monotone',\n strokewidth: 1.5,\n});\n\nspark.dispose();\n```\n\n### variants\n\n **`line`**: simple polyline path (default)\n **`area`**: gradient filled area + line overlay\n **`bar`**: vertical bar for each data point\n **`stack`**: horizontal proportional segments; use `stacksegment[]` for `data` with per segment colors\n\nline and area sparklines mark the latest value with a dot. set `showendpoint: false` to hide it.\n\n### updating data\n\n```ts\nconst spark = createsparkline(container, {\n data: [12, 18, 14, 22],\n variant: 'area',\n});\n\nspark.update([12, 18, 14, 22, 30]);\n```\n\n### event hooks\n\nsparklines use simplified hooks: `sparklineevent` rather than full `chartevent`. with a hook set, a marker follows the hovered value:\n\n```ts\nconst spark = createsparkline(container, {\n data: [10, 20, 30],\n onhover: (event) => {\n // event is sparklineevent | null (null on mouseleave)\n if (event) console.log(`hovering point ${event.index}: ${event.value}`);\n },\n onclick: (event) => {\n console.log(`clicked point ${event.index}: ${event.value}`);\n },\n});\n```\n\n`sparklineevent` provides `index`, the hovered/clicked `value`, and the `originalevent`.\n\n> **note:** sparklines have no keyboard navigation. label one with `a11y.arialabel` only when the trend carries meaning, and state the key value in surrounding text.\n\n## axes and grid\n\nboth axes render by default: the value axis (y on vertical charts, x on horizontal ones) carries gridlines, the category axis does not. set `xaxis: false` or `yaxis: false` to suppress one. an explicit config is used verbatim:\n\n```ts\n{\n xaxis: {\n position: 'bottom', // 'top' | 'bottom'\n tickcount: 5,\n tickformat: (v) => `$${v}`,\n label: 'month',\n grid: true, // or { color: '#ddd', dash: '4 2' }\n },\n yaxis: {\n position: 'left', // 'left' | 'right'\n grid: { color: '#f0f0f0' },\n label: 'revenue ($)',\n },\n}\n```\n\ncategory axes show every label when the labels fit and thin them out only when they would overlap. set `tickcount` to choose the density yourself.\n\n## tooltips\n\nenable with `tooltip: true`. on line, area, bar, and radar charts the default tooltip compares every series at the active key: a title, then a colour swatch, name, and value per series. provide a custom `render` function to replace it. strings are rendered as text. return a dom node for structured content:\n\n```ts\n{\n tooltip: {\n offset: 12,\n render: (datum, series) => {\n const content = document.createelement('strong');\n content.textcontent = `${series.name}: ${datum.value.tolocalestring()}`;\n return content;\n },\n },\n}\n```\n\nprism never injects tooltip strings as html, so custom rendering does not require a sanitizer.\n\nthe tooltip element is scoped inside the chart container (not `document.body`) and is removed automatically on `dispose()`.\n\n## crosshair\n\na vertical guide that snaps to the nearest data point:\n\n```ts\n{\n crosshair: true,\n // or configure:\n crosshair: { vertical: true, horizontal: true, snap: true },\n}\n```\n\n## legend\n\nenable with `legend: true` (defaults to `bottom`) or configure position:\n\n```ts\n{\n legend: true,\n // or:\n legend: { position: 'top' }, // 'top' | 'bottom' | 'left' | 'right'\n}\n```\n\nthe legend renders as a `div` placed outside the svg. each item shows a color swatch and the series `name`. customize via css:\n\n```css\n:root {\n prism legend gap: 1rem;\n prism legend dot size: 0.5rem;\n prism legend font size: 0.75rem;\n}\n```\n\n## event hooks\n\nall charts expose `onclick` and `onhover` callbacks on the config:\n\n```ts\nconst chart = createlinechart(container, {\n series: [{ name: 'revenue', data }],\n onhover: (event) => {\n // event is chartevent | null (null on mouseleave)\n if (event) console.log(event.datum, event.series);\n },\n onclick: (event) => {\n console.log('clicked', event.datum);\n },\n});\n```\n\n`chartevent` provides:\n\n `datum`: the datum of the series nearest the pointer\n `series`: the corresponding `series` config\n `values`: every series at the same key (line, area, and bar charts)\n `originalevent`: the raw `mouseevent` or, for keyboard navigation, `keyboardevent`\n\n> **pie, radar, and sparkline events differ**: pie hooks receive a `pieevent` (`index`, `slice`, `originalevent`), sparkline hooks a `sparklineevent` (`index`, `value`, `originalevent`), and radar hooks a `radarevent` describing an axis. see [`piechartconfig`](./api.md#chart configurations) and [`createradarchart()`](./api.md#createradarchart).\n\n## animations\n\nevery chart animates by default: bars grow from the baseline, line and area\nseries rise out of the plot, pie slices sweep in and then morph between values,\nradar polygons reshape, and sparklines tween. motion runs through one\n`requestanimationframe` loop and is skipped when the user prefers reduced motion\n(`preference: 'system'`, the default).\n\ntune it with `transition`, or turn it off entirely:\n\n```ts\n{\n transition: {\n duration: 400,\n easing: 'ease out',\n stagger: 30, // ms delay between bars (bar charts) or series (radar charts)\n },\n}\n```\n\n```ts\n{ transition: false } // every update renders synchronously\n{ transition: true } // the defaults, stated explicitly\n```\n\nnamed easings are `'linear'`, `'ease in'`, `'ease out'`, `'ease in out'`,\n`'expo out'` (a hard decelerate), and `'back out'` (a small overshoot); pass a\n`(t: number) => number` for anything else. `stagger` applies to bar chart bars\nand radar chart series and is ignored elsewhere — bar charts cap the total\nstaggered delay near 400 ms so a long category axis still finishes promptly.\n\n## theming\n\nimport the default theme:\n\n```ts\nimport '@vielzeug/prism/theme.css';\n```\n\n### programmatic theme with `settheme`\n\ncall `settheme` once at app startup to apply custom tokens programmatically:\n\n```ts\nimport { settheme } from '@vielzeug/prism';\n\nsettheme({\n axiscolor: '#94a3b8', // sets prism axis color\n colors: ['#6366f1', '#22d3ee', '#f59e0b', '#10b981'], // replaces prism color 1 through 4\n fontfamily: 'inter, system ui, sans serif', // sets prism font family\n gridcolor: '#e2e8f0', // sets prism grid color\n gridopacity: 0.6, // sets prism grid opacity\n textcolor: '#0f172a', // sets prism text color\n tooltipbg: '#0f172a', // sets prism tooltip bg\n});\n```\n\n`settheme` writes inline custom properties, so it takes precedence over stylesheet defaults. call `resettheme()` to clear every custom property `settheme` can set and restore the default theme: useful for a theme switcher's \"reset\" action or test teardown:\n\n```ts\nimport { resettheme } from '@vielzeug/prism';\n\nresettheme();\n```\n\n### theming one subtree (component libraries)\n\npass `scope` to write the tokens onto one element instead of `document.documentelement`. charts render inside it, so their `var( prism *)` lookups resolve against the scoped values without touching `:root` — the hook for apps whose theme lives in a js palette object (mui, chakra, backstage):\n\n```tsx\nimport { useeffect, useref } from 'react';\nimport { usetheme } from '@mui/material/styles';\nimport { createbarchart, resettheme, settheme, type barseriesconfig } from '@vielzeug/prism';\nimport '@vielzeug/prism/theme.css';\n\nfunction costschart({ series }: { series: barseriesconfig[] }) {\n const theme = usetheme();\n const containerref = useref<htmldivelement>(null);\n\n useeffect(() => {\n const scope = containerref.current;\n\n if (!scope) return;\n\n // scoped tokens win over :root inside this subtree and never leak out of it.\n settheme(\n { axiscolor: theme.palette.divider, textcolor: theme.palette.text.primary },\n { scope },\n );\n\n const chart = createbarchart(scope, { series });\n\n return () => {\n chart.dispose();\n resettheme({ scope });\n };\n }, [series, theme]);\n\n return <div ref={containerref} style={{ height: 300 }} />;\n}\n```\n\na scoped call and a global call are independent: `resettheme({ scope })` clears only what `settheme({ scope })` wrote.\n\n### custom theme (css)\n\n```css\n:root {\n prism color 1: #6366f1;\n prism color 2: #22c55e;\n prism axis color: #71717a;\n prism grid color: #f4f4f5;\n prism text color: #18181b;\n prism tooltip bg: #27272a;\n prism font family: 'inter', system ui, sans serif;\n}\n```\n\n### scoped themes\n\napply tokens to a specific container:\n\n```css\n.dark dashboard {\n prism axis color: #64748b;\n prism grid color: #334155;\n prism text color: #e2e8f0;\n}\n```\n\n### available tokens\n\n| token | default | description |\n| | | |\n| ` prism color {1 8}` | colorblind safe palette | series color palette |\n| ` prism bg` | `transparent` | chart background |\n| ` prism axis color` | `hsl(215deg 16% 47%)` | axis lines and ticks |\n| ` prism grid color` | `hsl(220deg 10% 75%)` | grid lines |\n| ` prism text color` | `hsl(215deg 25% 27%)` | axis labels and text |\n| ` prism font family` | `system ui` | chart font |\n| ` prism font size` | `0.75rem` | label font size |\n| ` prism tooltip bg` | `hsl(222deg 47% 11%)` | tooltip background |\n| ` prism tooltip color` | `hsl(210deg 40% 98%)` | tooltip text |\n| ` prism tooltip radius` | `0.375rem` | tooltip border radius |\n| ` prism crosshair color` | `hsl(215deg 16% 47%)` | crosshair line |\n| ` prism crosshair dash` | `4 2` | crosshair dash pattern |\n\nthe palette follows wong (2011) so adjacent series stay distinguishable under common color vision deficiencies. dark mode applies through `prefers color scheme`, an `html.dark` class, or `data prism theme=\"dark\"` on the chart container or any ancestor — the last is the hook for apps that switch theme in js without owning `<html>`.\n\nradar charts add their own tokens, each defaulting to a shared one:\n\n| token | default | description |\n| | | |\n| ` prism radar grid color` | ` prism grid color` | rings |\n| ` prism radar grid opacity` | `0.7` | ring and spoke opacity |\n| ` prism radar band fill` | ` prism grid color` | alternate ring shading |\n| ` prism radar band opacity` | `0.14` | shading opacity |\n| ` prism radar spoke color` | ` prism radar grid color` | spokes |\n| ` prism radar fill opacity` | ` prism area opacity` | solid shape fill |\n| ` prism radar stroke width` | ` prism line width` | shape outline |\n| ` prism radar point radius` | ` prism point radius` | vertex dots |\n| ` prism radar point radius active` | ` prism point radius hover` | dots on the active axis |\n| ` prism radar label color` / ` size` | secondary text tokens | axis labels |\n| ` prism radar value color` / ` size` | text tokens | vertex values |\n| ` prism radar dim opacity` | ` prism dim opacity` | other shapes while one is hovered |\n\nshared interaction and chart tokens:\n\n| token | default | description |\n| | | |\n| ` prism dim opacity` | `0.35` | series or slices outside the active one |\n| ` prism active point radius` | ` prism point radius hover` | points marked at the active key |\n| ` prism point ring` | `canvas` | ring separating active and end points from the line |\n| ` prism focus color` | ` prism color 1` | focus outline of a keyboard focused chart |\n| ` prism bar radius` | `4px` | bar corner radius (stacked bars stay square) |\n| ` prism bar band fill` / ` opacity` | grid colour / `0.18` | shaded active category |\n| ` prism bar dim opacity` | `0.55` | bars outside the active category |\n| ` prism area gradient start` / ` end` | `0.45` / `0.02` | gradient fill opacity at the line and the baseline |\n| ` prism spark gradient start` / ` end` | `0.35` / `0` | sparkline gradient fill |\n\nexplicit config such as `strokewidth`, `fillopacity`, or `borderradius` always wins over these tokens.\n\n## scales (standalone)\n\nscales can be used independently for custom visualizations:\n\n```ts\nimport { linearscale, timescale, bandscale } from '@vielzeug/prism';\n\nconst y = linearscale({ domain: [0, 100], range: [300, 0] });\ny.map(50); // → 150\ny.invert(150); // → 50\ny.ticks(5); // → [0, 20, 40, 60, 80, 100]\n\nconst x = bandscale({ domain: ['a', 'b', 'c'], range: [0, 300] });\nx.map('b'); // → pixel left edge of band b\nx.bandwidth(); // → width of each band\n```\n\n## lifecycle and cleanup\n\nevery chart returns a `charthandle`. always call `dispose()` when removing a chart:\n\n```ts\nconst chart = createlinechart(container, config);\n\n// when done:\nchart.dispose();\n\n// or with tc39 explicit resource management:\n{\n using chart = createlinechart(container, config);\n // auto disposed at block end\n}\n```\n\ncalling `dispose()`:\n\n cancels in flight transitions\n disconnects the `resizeobserver`\n removes the svg element, tooltip, and legend from the dom\n restores container styles changed for tooltip positioning\n is idempotent: safe to call multiple times\n\ncall `update()` only while the handle is active. updating a disposed chart throws `prismrendererror`.\n\n## responsive behavior\n\ncharts resize automatically when the container dimensions change. prism uses `resizeobserver` internally: no manual `resize()` call is needed.\n\n## runtime observation\n\nevery handle exposes `tap()` to observe runtime behavior — resize and dispose — outside the render path. it reuses the chart's own `resizeobserver`, so observing costs nothing extra, and handler errors are swallowed: observation never affects chart behavior.\n\n```ts\nimport { createlinechart } from '@vielzeug/prism';\n\nconst chart = createlinechart(container, {\n series: [{ data: [{ key: 1, value: 10 }], name: 'revenue' }],\n});\nconst controller = new abortcontroller();\nconst unsubscribe = chart.tap(\n (event) => {\n if (event.type === 'resize') console.log('resized', event.width, event.height);\n if (event.type === 'dispose') console.log('disposed');\n },\n { signal: controller.signal },\n);\n```\n\n`tap()` returns an unsubscribe function; pass `options.signal` to detach automatically instead. the `resize` event carries the new `width`/`height` and fires after the chart re laid out; `dispose` fires once, before teardown completes. tapping a disposed handle returns a no op unsubscribe. this is separate from prism's internal validation warnings, which run automatically in development and are never part of the public api.\n\n## accessibility\n\nlabel informative charts with `a11y: { arialabel }`. a labelled chart renders with `role=\"img\"`, is focusable, and supports keyboard navigation. charts without `a11y` are decorative and render with `aria hidden=\"true\"`.\n\n```ts\ncreatelinechart(container, {\n a11y: { arialabel: 'revenue by month' },\n series: [...],\n});\n```\n\nmark a chart decorative explicitly when surrounding text already states its meaning:\n\n```ts\ncreatesparkline(container, {\n a11y: { decorative: true },\n data: [...],\n});\n```\n\n### keyboard and announcements\n\nevery labelled chart except sparklines shares one keyboard model:\n\n| chart | arrow keys move between | `home` / `end` | announced |\n| | | | |\n| line, area | x positions | first / last position | `1: adam 3, eve 6` |\n| bar | categories | first / last category | `str: adam 8, eve 5` |\n| pie | slices | first / last slice | `wins: 15 (75%)` |\n| radar | axes (wrapping) | not supported | `agility: adam 6, eve 9` |\n\nthe first arrow press focuses the first item. `enter` or `space` fires `onclick` for the focused item, and `escape` clears it. the tooltip speaks the text when one is enabled; otherwise the chart's own `role=\"status\"` region (`.prism live`) does, so each value is announced once.\n\n### hover feedback\n\nthe active key is shown the same way on every chart. line and area charts mark each series' point and dim the series farther from the pointer. bar charts shade the category band and dim the other categories. pie charts dim the other slices. radar charts highlight the active spoke and its points, and dim the other shapes while one is hovered.\n\n## framework integration\n\nprism has no framework adapter. connect the same three operations to your framework's lifecycle: create after mount, update when state changes, and dispose before unmount.\n\n```ts\nimport { createlinechart, type charthandle, type continuousdatum, type lineseriesconfig } from '@vielzeug/prism';\n\nlet chart: charthandle<lineseriesconfig[]> | undefined;\n\nexport function mountchart(container: htmlelement, data: continuousdatum[]): void {\n chart = createlinechart(container, { series: [{ data, name: 'series' }] });\n}\n\nexport function updatechart(data: continuousdatum[]): void {\n chart?.update([{ data, name: 'series' }]);\n}\n\nexport function unmountchart(): void {\n chart?.dispose();\n chart = undefined;\n}\n```\n\n## working with other vielzeug libraries\n\n### with ripple\n\nkeep state ownership in ripple and connect it to prism through an explicit subscription.\n\n```ts\nimport { createlinechart } from '@vielzeug/prism';\nimport { signal } from '@vielzeug/ripple';\n\nconst data = signal([{ key: 1, value: 10 }]);\nconst toseries = () => [{ data: data.value, name: 'series' }];\nconst chart = createlinechart(container, { series: toseries() });\nconst unsubscribe = data.subscribe(() => chart.update(toseries()));\n\n// cleanup both owners together.\nunsubscribe();\nchart.dispose();\n```\n\n### with sourcerer\n\nbind chart data to a sourcerer remote source so charts update whenever the list refreshes.\n\n```ts\nimport { createbarchart } from '@vielzeug/prism';\nimport { createpagesource } from '@vielzeug/sourcerer';\n\nconst source = createpagesource({\n load: async ({ page, pagesize, signal }) => {\n const result = await api.stats.list({ page, pagesize }, { signal });\n return { items: result.data, totalitems: result.total };\n },\n});\nconst toseries = (state) => [\n {\n data: state.items.map((item) => ({ key: item.label, value: item.count })),\n name: 'series',\n },\n];\nconst chart = createbarchart(container, { series: toseries(source.state) });\nconst unsubscribe = source.subscribe((state) => chart.update(toseries(state)));\nvoid source.reload().catch(() => undefined);\n```\n\n## best practices\n\n ensure the container element has explicit dimensions before calling a chart factory: `resizeobserver` needs a non zero layout size to trigger the first render.\n call `chart.dispose()` in your framework's unmount/cleanup phase to cancel transitions, disconnect resize observation, and remove dom nodes.\n call `chart.update(data)` from your application state boundary; prism does not require or own a state library.\n set `a11y: { arialabel: '…' }` on every chart that conveys meaningful data: unlabelled charts are hidden from assistive technology and receive no keyboard navigation.\n observe lifecycle with `chart.tap(handler)` rather than polling `chart.disposed` or wrapping the container: it costs nothing when untapped and detaches via `signal`.\n for ssr, skip chart creation server side: prism depends on dom apis and `resizeobserver`. render charts only after hydration in a `onmounted`/`useeffect` callback.\n",
944
- "examples": " \ntitle: 'prism: examples'\ndescription: focused recipes for creating, updating, interacting with, and theming prism charts.\n \n\n [create a line chart](./examples/line chart.md)\n [plot a time series](./examples/time series.md)\n [compare categories with a bar chart](./examples/bar chart.md)\n [render compact charts](./examples/compact charts.md)\n [compare hero stats with a radar chart](./examples/radar chart.md)\n [update chart data](./examples/update data.md)\n [sync a details panel with hover](./examples/hover details.md)\n [render a custom tooltip](./examples/custom tooltip.md)\n [match charts to your design system](./examples/theming.md)\n"
941
+ "index": " \ntitle: 'prism: responsive svg data visualization'\ndescription: 'responsive svg charts with explicit updates: line, bar, area, scatter, pie, radar, sparkline'\npackage: prism\ncategory: ui\nkeywords: [chart, svg, visualization, responsive, line chart, bar chart, area chart, scatter chart, pie chart, radar chart, sparkline, accessibility, typescript]\nrelated: [refine, orbit]\nexports: [createlinechart, createbarchart, createpiechart, charthandle]\nenvironments: [browser]\n \n\n<! markdownlint disable md025 md033 md060 >\n\n<packagehero package=\"prism\" />\n\n## why prism?\n\nprism renders charts as svg in a host element and keeps application state outside the chart. you can replace chart data explicitly, respond to container resizing, and style the output with css custom properties.\n\n```ts\nimport { createlinechart } from '@vielzeug/prism';\n\nconst host = document.createelement('div');\nhost.style.csstext = 'width:640px;height:320px';\ndocument.body.append(host);\n\n// before: replace a hand drawn svg each time values change.\nfunction drawline(values: number[]) {\n const path = document.createelementns('http://www.w3.org/2000/svg', 'path');\n const svg = document.createelementns('http://www.w3.org/2000/svg', 'svg');\n const low = math.min(...values);\n const span = math.max(...values) low || 1;\n const points = values.map((value, index) => {\n const x = (index / math.max(1, values.length 1)) * 640;\n const y = 320 ((value low) / span) * 320;\n return `${index === 0 ? 'm' : 'l'}${x},${y}`;\n });\n svg.setattribute('viewbox', '0 0 640 320');\n path.setattribute('d', points.join(' '));\n path.setattribute('fill', 'none');\n path.setattribute('stroke', 'currentcolor');\n svg.append(path);\n host.replacechildren(svg);\n}\ndrawline([10, 25, 18]);\ndrawline([10, 25, 18, 32]);\n\n// after: keep the chart instance and replace its series data.\nconst chart = createlinechart(host, {\n a11y: { arialabel: 'revenue by month' },\n series: [{ data: [{ key: 1, value: 10 }, { key: 2, value: 25 }, { key: 3, value: 18 }], name: 'revenue' }],\n});\nchart.update([{\n data: [{ key: 1, value: 10 }, { key: 2, value: 25 }, { key: 3, value: 18 }, { key: 4, value: 32 }],\n name: 'revenue',\n}]);\nchart.dispose();\n```\n\n| feature | prism | hand written svg |\n| | | |\n| bundle size | <packageinfo package=\"prism\" type=\"size\" /> | no chart package |\n| zero dependencies | <ore icon name=\"x\" size=\"16\"></ore icon> (uses orbit) | <ore icon name=\"check\" size=\"16\"></ore icon> |\n| data updates | `charthandle.update()` replaces chart data | you implement the update path |\n| responsive sizing | `resizeobserver` recalculates the chart layout | you implement the resize behavior |\n| chart interactions | built in pointer and keyboard interactions on supported charts | you implement them |\n| svg accessibility | informative charts support an accessible name and keyboard focus | you implement the accessible behavior |\n\n<div class=\"decision callout\">\n\n**use prism when** you need chart factories, responsive svg output, and an explicit data update boundary without adopting a charting framework.\n\n**consider hand written svg when** you need a single specialized graphic and prefer to own its scales, resizing, labels, and interactions directly.\n\n</div>\n\n## installation\n\n::: code group\n\n```sh [pnpm]\npnpm add @vielzeug/prism\n```\n\n```sh [npm]\nnpm install @vielzeug/prism\n```\n\n```sh [yarn]\nyarn add @vielzeug/prism\n```\n\n:::\n\n## quick start\n\ncreate and size a host element before creating the chart. import `theme.css` once to load prism's default styles and css custom properties.\n\n```ts\nimport { createlinechart } from '@vielzeug/prism';\nimport '@vielzeug/prism/theme.css';\n\nconst host = document.createelement('div');\nhost.style.csstext = 'width:640px;height:320px';\ndocument.body.append(host);\n\nconst chart = createlinechart(host, {\n a11y: { arialabel: 'revenue by month' },\n series: [{\n data: [{ key: 1, value: 10 }, { key: 2, value: 25 }, { key: 3, value: 18 }],\n name: 'revenue',\n }],\n tooltip: true,\n});\n\nchart.update([{\n data: [{ key: 1, value: 10 }, { key: 2, value: 25 }, { key: 3, value: 18 }, { key: 4, value: 32 }],\n name: 'revenue',\n}]);\n\nchart.dispose();\n```\n\n## features\n\n<div class=\"features grid\">\n\n **`createlinechart()` / `createareachart()`**: plot continuous numeric or date keyed series\n **`createbarchart()`**: compare categories with grouped, stacked, or horizontal bars\n **`createscatterchart()`**: plot independent numeric `(x, y)` points\n **`createpiechart()`**: render pie, donut, or semi circle slices\n **`createradarchart()`**: compare series across fixed, labelled axes\n **`createsparkline()`**: render compact line, area, bar, or stacked values without axes\n **`charthandle.update(data)`**: replace chart data without handing state ownership to prism\n **`charthandle.dispose()` / `symbol.dispose`**: release the svg, observer, overlays, and chart owned animation\n **`charthandle.tap()`**: observe resize and disposal events\n **`settheme()` / `resettheme()`**: set supported palette, typography, axis, grid, and tooltip tokens globally or on one element\n **css custom properties**: style the chart palette, marks, axes, labels, tooltip, legend, and motion\n **resize observation**: recalculate svg layout when the host element changes size\n **accessible interaction**: labelled interactive charts expose keyboard navigation and polite value announcements; sparklines are pointer only without value announcements\n\n</div>\n\n## documentation\n\n<div class=\"doc links\">\n\n [usage guide](./usage.md)\n [api reference](./api.md)\n [examples](./examples.md)\n [migration guide](./migration.md)\n\n</div>\n\n## see also\n\n<div class=\"see also\">\n\n [refine](/refine/): web components and theme tokens for building controls and layouts around chart hosts\n [orbit](/orbit/): floating element positioning used internally by prism's built in tooltip\n\n</div>\n\n<! markdownlint enable md025 md033 md060 >\n",
942
+ "api": " \ntitle: 'prism: api reference'\ndescription: complete reference for prism chart factories, scales, themes, types, and errors.\n \n\n[[toc]]\n\n## api overview\n\nall chart factories, scale utilities, theme helpers, and error classes are synchronous. charts render into an html element and return a `charthandle`; they do not return a promise.\n\n| symbol | purpose | execution mode | common gotcha |\n| | | | |\n| `createlinechart()` | render one or more continuous line series | sync | `key` values must be numbers or dates |\n| `createareachart()` | render continuous series with filled areas | sync | fills use a gradient by default |\n| `createscatterchart()` | plot independent `(x, y)` points | sync | `key` is the x value; the y domain is fitted to data |\n| `createbarchart()` | render categorical bars | sync | negative values in stacked variants are clamped to zero |\n| `createpiechart()` | render pie, donut, or semi circle slices | sync | `variant` is fixed at creation; `update()` replaces slices |\n| `createradarchart()` | compare series across fixed axes | sync | requires at least three axes to draw |\n| `createsparkline()` | render a compact line, area, bar, or stack | sync | has no axes, legend, or keyboard navigation |\n| `linearscale()` | create a numeric scale | sync | `nice` defaults to `true`; set `nice: false` to preserve the domain |\n| `timescale()` | create a date scale | sync | `nice` defaults to `true` |\n| `bandscale()` | create an ordered categorical scale | sync | unknown categories map to `0` and warn in development |\n| `settheme()` | set prism css custom properties | sync | writes inline styles to `document.documentelement` by default |\n| `resettheme()` | remove theme overrides set by `settheme()` | sync | only clears the selected target |\n| `seriescolor()` | resolve a palette css value | sync | palette positions wrap after eight |\n| `prismerror` | base class for prism errors | sync | use `instanceof` to catch any prism error |\n| `prismrendererror` | report an invalid chart container or failed chart operation | sync | also thrown by `update()` after disposal |\n\n## package entry point\n\n| import | purpose |\n| | |\n| `@vielzeug/prism` | chart factories, scale and theme functions, error classes, and public types |\n| `@vielzeug/prism/theme.css` | default chart styles, css custom properties, dark mode values, and reduced motion rules |\n\n## chart factories\n\neach factory appends an svg to `container`. a chart with no measurable container size uses a temporary `600 × 300` svg size and emits a development warning; `resizeobserver` applies later size changes. unless `a11y` is informative, the svg is hidden from the accessibility tree.\n\n### `createlinechart()`\n\n```ts\nfunction createlinechart(\n container: htmlelement,\n config: linechartconfig,\n): charthandle<lineseriesconfig[]>;\n```\n\nrenders line series and returns a handle whose `update()` argument replaces the complete series array. axes render by default; the value axis has grid lines by default. pass `false` to `xaxis` or `yaxis` to suppress that axis.\n\nseries use linear interpolation by default, omit point markers by default, and use a point radius of 3 when markers are enabled. `yaxis` defaults to `'left'`; crosshair, tooltip, and legend are disabled unless configured.\n\n| parameter | type | description |\n| | | |\n| `container` | `htmlelement` | host that receives the chart svg and optional overlays |\n| `config` | `linechartconfig` | series, axes, interaction, accessibility, and motion settings |\n\n**returns:** `charthandle<lineseriesconfig[]>`.\n\n```ts\nimport { createlinechart } from '@vielzeug/prism';\n\nconst host = document.createelement('div');\nhost.style.csstext = 'width:640px;height:320px';\ndocument.body.append(host);\n\nconst chart = createlinechart(host, {\n a11y: { arialabel: 'revenue by month' },\n series: [{ data: [{ key: 1, value: 10 }, { key: 2, value: 15 }], name: 'revenue' }],\n});\nchart.update([{ data: [{ key: 1, value: 10 }, { key: 2, value: 15 }, { key: 3, value: 18 }], name: 'revenue' }]);\nchart.dispose();\n```\n\n### `createareachart()`\n\n```ts\nfunction createareachart(\n container: htmlelement,\n config: areachartconfig,\n): charthandle<areaseriesconfig[]>;\n```\n\nrenders continuous area series with a gradient fill by default. `update()` replaces the complete series array.\n\narea series use linear interpolation by default and show their top line unless `showline: false` is set. axes render by default, with grid lines on the value axis.\n\n| parameter | type | description |\n| | | |\n| `container` | `htmlelement` | host that receives the chart svg and optional overlays |\n| `config` | `areachartconfig` | series, axes, interaction, accessibility, and motion settings |\n\n**returns:** `charthandle<areaseriesconfig[]>`.\n\n```ts\nimport { createareachart } from '@vielzeug/prism';\n\nconst host = document.createelement('div');\nhost.style.csstext = 'width:640px;height:320px';\ndocument.body.append(host);\n\nconst chart = createareachart(host, {\n a11y: { arialabel: 'visitors over time' },\n series: [{ data: [{ key: 1, value: 10 }, { key: 2, value: 18 }], name: 'visitors' }],\n});\nchart.dispose();\n```\n\n### `createscatterchart()`\n\n```ts\nfunction createscatterchart(\n container: htmlelement,\n config: scatterchartconfig,\n): charthandle<scatterseriesconfig[]>;\n```\n\nplots each datum independently: `key` is the numeric or date x value and `value` is the y value. points are not joined. `update()` replaces the complete series array.\n\naxes render by default, with grid lines on the value axis. the value scale is fitted to the observed data rather than forced to include zero. tooltip, crosshair, and legend are disabled unless configured.\n\n| parameter | type | description |\n| | | |\n| `container` | `htmlelement` | host that receives the chart svg and optional overlays |\n| `config` | `scatterchartconfig` | independent points, axes, interaction, accessibility, and motion settings |\n\n**returns:** `charthandle<scatterseriesconfig[]>`.\n\n```ts\nimport { createscatterchart } from '@vielzeug/prism';\n\nconst host = document.createelement('div');\nhost.style.csstext = 'width:640px;height:320px';\ndocument.body.append(host);\n\nconst chart = createscatterchart(host, {\n a11y: { arialabel: 'height against weight' },\n series: [{ data: [{ key: 160, value: 55 }, { key: 175, value: 70 }], name: 'cohort a' }],\n tooltip: true,\n});\nchart.dispose();\n```\n\nscatter interaction reports one datum and series rather than a `values` array. keyboard navigation visits points across series in x order.\n\n### `createbarchart()`\n\n```ts\nfunction createbarchart(\n container: htmlelement,\n config: barchartconfig,\n): charthandle<barseriesconfig[]>;\n```\n\nrenders grouped, stacked, grouped horizontal, or stacked horizontal categories. `update()` replaces the complete series array; the variant is fixed at creation. axes render by default, with grid lines on the value axis.\n\nthe default variant is `grouped`; tooltip and legend are disabled unless configured.\n\n| parameter | type | description |\n| | | |\n| `container` | `htmlelement` | host that receives the chart svg and optional overlays |\n| `config` | `barchartconfig` | series, layout variant, axes, interaction, accessibility, and motion settings |\n\n**returns:** `charthandle<barseriesconfig[]>`.\n\n```ts\nimport { createbarchart } from '@vielzeug/prism';\n\nconst host = document.createelement('div');\nhost.style.csstext = 'width:640px;height:320px';\ndocument.body.append(host);\n\nconst chart = createbarchart(host, {\n a11y: { arialabel: 'tasks by status' },\n series: [{ data: [{ key: 'open', value: 12 }, { key: 'done', value: 8 }], name: 'tasks' }],\n});\nchart.dispose();\n```\n\nfor stacked variants, negative values are treated as zero and produce a development warning.\n\n### `createpiechart()`\n\n```ts\nfunction createpiechart(\n container: htmlelement,\n config: piechartconfig,\n): charthandle<piesliceconfig[]>;\n```\n\nrenders pie, donut, or semi circle slices. the default variant is `pie`; `update()` replaces the slice array. the default inner radius is zero for pies and 55% of the outer radius for donut and semi variants. pie slices default to no padding or rounded corners; donut and semi slices default to 8px padding and corner radius.\n\n| parameter | type | description |\n| | | |\n| `container` | `htmlelement` | host that receives the chart svg and optional overlays |\n| `config` | `piechartconfig` | slices, shape, geometry, legend, tooltip, and interaction settings |\n\n**returns:** `charthandle<piesliceconfig[]>`.\n\n```ts\nimport { createpiechart } from '@vielzeug/prism';\n\nconst host = document.createelement('div');\nhost.style.csstext = 'width:320px;height:240px';\ndocument.body.append(host);\n\nconst chart = createpiechart(host, {\n a11y: { arialabel: 'orders by channel' },\n data: [{ label: 'direct', value: 72 }, { label: 'referral', value: 28 }],\n variant: 'donut',\n});\nchart.dispose();\n```\n\n### `createradarchart()`\n\n```ts\nfunction createradarchart(\n container: htmlelement,\n config: radarchartconfig,\n): charthandle<radarseriesconfig[]>;\n```\n\nrenders one shape per series across the configured axes. `update()` replaces series data but not axes or other configuration. fewer than three axes warn in development and draw no chart. tooltip and legend are disabled unless configured.\n\n| parameter | type | description |\n| | | |\n| `container` | `htmlelement` | host that receives the chart svg and optional overlays |\n| `config` | `radarchartconfig` | fixed axes, series, scale, grid, appearance, and interaction settings |\n\n**returns:** `charthandle<radarseriesconfig[]>`.\n\n```ts\nimport { createradarchart } from '@vielzeug/prism';\n\nconst host = document.createelement('div');\nhost.style.csstext = 'width:400px;height:320px';\ndocument.body.append(host);\n\nconst chart = createradarchart(host, {\n a11y: { arialabel: 'hero stats' },\n axes: [{ key: 'str', label: 'strength' }, { key: 'agi', label: 'agility' }, { key: 'end', label: 'endurance' }],\n domain: [0, 10],\n series: [{ data: [{ key: 'str', value: 8 }, { key: 'agi', value: 6 }, { key: 'end', value: 9 }], name: 'adam' }],\n});\nchart.dispose();\n```\n\nradar defaults are a solid fill, linear curves, visible points, a four level polygon grid with bands, no grid labels, and a first axis angle of zero degrees. grid labels render only when all axes share a domain.\n\n### `createsparkline()`\n\n```ts\nfunction createsparkline(\n container: htmlelement,\n config: sparklineconfig,\n): charthandle<number[] | stacksegment[]>;\n```\n\nrenders compact data without axes, chart margins, or a legend. `update()` replaces the number or stack segment array. the default variant is `line`; only line and area variants draw the endpoint marker by default.\n\n| parameter | type | description |\n| | | |\n| `container` | `htmlelement` | host that receives the chart svg |\n| `config` | `sparklineconfig` | values, variant, appearance, accessibility, and pointer callbacks |\n\n**returns:** `charthandle<number[] | stacksegment[]>`.\n\n```ts\nimport { createsparkline } from '@vielzeug/prism';\n\nconst host = document.createelement('div');\nhost.style.csstext = 'width:240px;height:48px';\ndocument.body.append(host);\n\nconst chart = createsparkline(host, { data: [2, 5, 3, 8], variant: 'area' });\nchart.update([2, 5, 3, 8, 13]);\nchart.dispose();\n```\n\n`stacksegment[]` is for `variant: 'stack'`. pointer callbacks are available for numeric line, area, and bar sparklines; sparklines do not support keyboard navigation.\n\n## scale factories\n\n### `linearscale()`\n\n```ts\nfunction linearscale(config: linearscaleconfig): scale<number>;\n```\n\nmaps a numeric domain to a numeric range. `nice` defaults to `true`; `clamp` defaults to `false` and applies to `map()`, not `invert()`.\n\n| parameter | type | default | description |\n| | | | |\n| `config.domain` | `[number, number]` | required | input extent |\n| `config.range` | `[number, number]` | required | output extent |\n| `config.clamp` | `boolean` | `false` | clamp mapped values to the range |\n| `config.nice` | `boolean` | `true` | expand the domain to rounded boundaries |\n\n**returns:** `scale<number>`, with `map`, `invert`, `ticks`, `domain`, and `range`.\n\n```ts\nimport { linearscale } from '@vielzeug/prism';\n\nconst scale = linearscale({ domain: [0, 100], range: [0, 500] });\nscale.map(50); // 250\n```\n\n### `timescale()`\n\n```ts\nfunction timescale(config: timescaleconfig): scale<date>;\n```\n\nmaps date values to a numeric range. `nice` defaults to `true` and rounds the domain to a time interval.\n\n| parameter | type | default | description |\n| | | | |\n| `config.domain` | `[date, date]` | required | input date extent |\n| `config.range` | `[number, number]` | required | output extent |\n| `config.nice` | `boolean` | `true` | expand the domain to time interval boundaries |\n\n**returns:** `scale<date>`, with `map`, `invert`, `ticks`, `domain`, and `range`.\n\n```ts\nimport { timescale } from '@vielzeug/prism';\n\nconst scale = timescale({\n domain: [new date('2026 01 01'), new date('2026 12 31')],\n range: [0, 500],\n});\nscale.map(new date('2026 07 02'));\n```\n\n### `bandscale()`\n\n```ts\nfunction bandscale(config: bandscaleconfig): bandscale;\n```\n\nmaps ordered string categories to the start of each band. `padding` defaults to `0.1`; `paddingouter` defaults to the same value. mapping an unknown category returns `0` and warns in development.\n\n| parameter | type | default | description |\n| | | | |\n| `config.domain` | `string[]` | required | ordered categories |\n| `config.range` | `[number, number]` | required | output extent |\n| `config.padding` | `number` | `0.1` | inner gap as a ratio of bandwidth |\n| `config.paddingouter` | `number` | `padding` | outer gap as a ratio of bandwidth |\n\n**returns:** `bandscale`, with `map`, `ticks`, `bandwidth`, `gap`, `domain`, and `range`.\n\n```ts\nimport { bandscale } from '@vielzeug/prism';\n\nconst scale = bandscale({ domain: ['a', 'b'], range: [0, 200] });\nscale.bandwidth();\n```\n\n## theme utilities\n\n`theme.css` supplies the base css values and styles. `settheme()` writes inline custom properties, which take precedence over stylesheet declarations. for other visual controls, set css custom properties directly.\n\n### `settheme()`\n\n```ts\nfunction settheme(theme: prismtheme, options?: themescope): void;\n```\n\nsets the provided theme values on `options.scope`, or on `document.documentelement` when omitted. omitted scalar keys remain unchanged. providing `colors` replaces all eight palette slots, removing unused slots.\n\n| parameter | type | description |\n| | | |\n| `theme` | `prismtheme` | palette and supported axis, grid, text, font, and tooltip properties |\n| `options.scope` | `htmlelement` | element that scopes the inline custom properties |\n\n**returns:** `void`.\n\n```ts\nimport { settheme } from '@vielzeug/prism';\n\nconst host = document.createelement('div');\ndocument.body.append(host);\nsettheme({ colors: ['#2563eb', '#16a34a'], textcolor: '#111827' }, { scope: host });\n```\n\n### `resettheme()`\n\n```ts\nfunction resettheme(options?: themescope): void;\n```\n\nremoves every custom property managed by `settheme()` from the given scope or the document root. it does not remove css rules or change any other custom properties.\n\n| parameter | type | description |\n| | | |\n| `options` | `themescope` | optional target; defaults to `document.documentelement` |\n\n**returns:** `void`.\n\n```ts\nimport { resettheme, settheme } from '@vielzeug/prism';\n\nconst scope = document.createelement('div');\nsettheme({ textcolor: '#111827' }, { scope });\nresettheme({ scope });\n```\n\n### `seriescolor()`\n\n```ts\nfunction seriescolor(index: number, override?: string): string;\n```\n\nreturns `override` when it is provided; otherwise returns a `var( prism color n)` reference. the palette has eight positions and the index wraps around after the eighth.\n\n| parameter | type | description |\n| | | |\n| `index` | `number` | zero based palette position |\n| `override` | `string` | optional color returned instead of a css variable |\n\n**returns:** `string`.\n\n```ts\nimport { seriescolor } from '@vielzeug/prism';\n\nseriescolor(0); // 'var( prism color 1)'\nseriescolor(1, '#2563eb'); // '#2563eb'\n```\n\n## chart handle\n\n`charthandle<tdata>` is returned by every chart factory. `update()` synchronously rebuilds the chart from its new data; enabled motion may animate the resulting svg. `dispose()` is idempotent, removes the chart svg, observers, tooltip and legend, cancels chart owned animation, aborts `disposalsignal`, and emits one `dispose` tap event. `update()` after disposal throws `prismrendererror`.\n\n`tap()` observes `resize` and `dispose` events outside the render path. it returns an unsubscribe function; an optional abort signal removes the subscription. observer handler exceptions are swallowed so they cannot affect chart behavior. a tap added after disposal returns a no op unsubscribe function.\n\n`[symbol.dispose]()` delegates to `dispose()`, allowing explicit resource management where supported.\n\nall chart handles expose the same lifecycle members; interactive cartesian and pie/radar charts use a `.prism live` status region when no tooltip is enabled. a tooltip is itself a polite status region. sparklines have neither a tooltip nor a live value announcer.\n\n| method or property | contract |\n| | |\n| `el` | the chart's root `svgsvgelement` |\n| `update(data)` | replace chart data and render; throws `prismrendererror` after disposal |\n| `dispose()` | idempotently release chart owned dom, observation, and motion |\n| `disposed` | whether the handle has been disposed |\n| `disposalsignal` | `abortsignal` aborted during disposal |\n| `tap(handler, options?)` | observe resize/dispose events; returns an unsubscribe function |\n| `[symbol.dispose]()` | calls `dispose()` |\n\n`prismevent` reports a changed svg content size as `{ type: 'resize', width, height }`; the `dispose` event is emitted once before teardown completes.\n\n## types\n\nthe following are all types and interfaces exported from `@vielzeug/prism`. definitions mirror the package entry point contracts.\n\n### animation types\n\n```ts\nexport type easingfn = (t: number) => number;\nexport type easingname = 'back out' | 'ease in' | 'ease in out' | 'ease out' | 'expo out' | 'linear';\n\nexport interface transitionconfig {\n /** milliseconds per animated lane. defaults to 300 (420 for a line entrance, 400 for radar). */\n duration?: number;\n easing?: easingname | ((t: number) => number);\n /** `system` skips animation when reduced motion is preferred. */\n preference?: 'always' | 'never' | 'system';\n /** per bar delay on bar charts or per series delay on radar charts. */\n stagger?: number;\n}\n\nexport type transitionoption = transitionconfig | boolean;\n```\n\n### theme types\n\n```ts\nexport interface prismtheme {\n axiscolor?: string;\n colors?: string[];\n fontfamily?: string;\n gridcolor?: string;\n gridopacity?: number;\n textcolor?: string;\n textcolorsecondary?: string;\n tooltipbg?: string;\n tooltipborder?: string;\n tooltipcolor?: string;\n}\n\nexport interface themescope {\n readonly scope?: htmlelement;\n}\n```\n\n`prismtheme.colors` writes up to eight palette slots. the scalar fields map as follows:\n\n| field | css custom property |\n| | |\n| `axiscolor` | ` prism axis color` |\n| `fontfamily` | ` prism font family` |\n| `gridcolor` | ` prism grid color` |\n| `gridopacity` | ` prism grid opacity` |\n| `textcolor` | ` prism text color` |\n| `textcolorsecondary` | ` prism text color secondary` |\n| `tooltipbg` | ` prism tooltip bg` |\n| `tooltipborder` | ` prism tooltip border` |\n| `tooltipcolor` | ` prism tooltip color` |\n\n### core and data types\n\n```ts\nexport interface chartmargin {\n bottom: number;\n left: number;\n right: number;\n top: number;\n}\n\nexport interface chartdimensions {\n height: number;\n margin: chartmargin;\n width: number;\n}\n\nexport type prismevent =\n | { readonly height: number; readonly type: 'resize'; readonly width: number }\n | { readonly type: 'dispose' };\n\nexport interface charthandle<tdata = unknown> {\n readonly disposalsignal: abortsignal;\n dispose(): void;\n readonly disposed: boolean;\n readonly el: svgsvgelement;\n tap(handler: (event: prismevent) => void, options?: { readonly signal?: abortsignal }): () => void;\n update(data: tdata): void;\n [symbol.dispose](): void;\n}\n\nexport type charta11y =\n | { readonly decorative: true }\n | { readonly arialabel: string; readonly decorative?: false };\n\nexport interface datum<tkey extends date | number | string = date | number | string> {\n dash?: string;\n key: tkey;\n meta?: record<string, unknown>;\n opacity?: number;\n value: number;\n}\n\nexport type continuousdatum = datum<date | number>;\n\nexport interface series<tdatum extends datum = datum> {\n color?: string;\n data: tdatum[];\n id?: string;\n name: string;\n}\n\nexport interface chartevent {\n datum: datum;\n originalevent: event;\n series: series;\n values?: seriesvalue[];\n}\n\nexport interface seriesvalue {\n datum: datum | undefined;\n series: series;\n}\n```\n\n`datum.dash` and `datum.opacity` affect rendered marks only on the chart types that support them. on bars, `dash` adds a 1px series color stroke so the dash is visible against the fill. `series.id` becomes a `data series id` attribute on a series group; characters outside `[a za z0 9_ ]` are replaced with ` `. when omitted, the id is based on the series index.\n\n| chart | `dash` | `opacity` |\n| | | |\n| bar | strokes that datum's bar | applies to that bar |\n| line | applies to the segment from this datum to the next | applies to that segment and its point marker |\n| area | applies to the top line segment from this datum to the next | applies to that segment |\n| scatter | ignored | applies to that point |\n| pie, radar, sparkline | ignored | ignored |\n\nfor line and area charts, the last datum has no following segment, so its `dash` value is ignored. the chart factories group adjacent segments with the same presentation values into path runs.\n\nfor line, area, and bar charts, `chartevent.values` lists each series at the active key in series order. its `datum` is `undefined` when that series has no matching key. `originalevent` is a pointer event or a keyboard event for keyboard activation. scatter events identify only the nearest independent point and omit `values`.\n\n### scale types\n\n```ts\nexport interface scale<t> {\n readonly domain: readonly [t, t];\n invert(pixel: number): t;\n map(value: t): number;\n readonly range: readonly [number, number];\n ticks(count?: number): t[];\n}\n\nexport interface bandscale {\n bandwidth(): number;\n readonly domain: readonly string[];\n gap(): number;\n map(value: string): number;\n readonly range: readonly [number, number];\n ticks(count?: number): string[];\n}\n\nexport interface linearscaleconfig {\n clamp?: boolean;\n domain: [number, number];\n nice?: boolean;\n range: [number, number];\n}\n\nexport interface timescaleconfig {\n domain: [date, date];\n nice?: boolean;\n range: [number, number];\n}\n\nexport interface bandscaleconfig {\n domain: string[];\n padding?: number;\n paddingouter?: number;\n range: [number, number];\n}\n```\n\n### axis and interaction types\n\n```ts\nexport type horizontalaxisposition = 'bottom' | 'top';\nexport type verticalaxisposition = 'left' | 'right';\nexport type axisposition = horizontalaxisposition | verticalaxisposition;\n\nexport interface gridconfig {\n color?: string;\n dash?: string;\n}\n\nexport interface axisconfig<tposition extends axisposition = axisposition> {\n grid?: gridconfig | boolean;\n label?: string;\n position?: tposition;\n tickcount?: number;\n tickformat?: (value: date | number | string) => string;\n}\n\nexport type xaxisconfig = axisconfig<horizontalaxisposition>;\nexport type yaxisconfig = axisconfig<verticalaxisposition>;\n\nexport interface tooltipconfig {\n offset?: number;\n render?: (datum: datum, series: series) => node | string;\n}\n\nexport interface crosshairconfig {\n horizontal?: boolean;\n snap?: boolean;\n vertical?: boolean;\n}\n\nexport type legendposition = 'bottom' | 'left' | 'right' | 'top';\n\nexport interface legendconfig {\n position?: legendposition;\n}\n```\n\nthe crosshair defaults to a vertical line that snaps to the nearest datum; the horizontal line is hidden by default. with `snap: false`, only the guide follows the pointer—tooltip and callback data still refer to the nearest datum.\n\ntooltip strings are assigned as text, not parsed as html. for a radar chart, `tooltip.render` receives a datum whose `meta` contains the active axis, all series values, and the default announcement text.\n\n### chart configuration types\n\n```ts\nexport interface basechartconfig {\n a11y?: charta11y;\n legend?: legendconfig | boolean;\n margin?: partial<chartmargin>;\n onclick?: (event: chartevent) => void;\n onhover?: (event: chartevent | null) => void;\n tooltip?: tooltipconfig | boolean;\n transition?: transitionoption;\n xaxis?: xaxisconfig | false;\n yaxis?: yaxisconfig | false;\n}\n\nexport interface lineseriesconfig extends series<continuousdatum> {\n curve?: 'linear' | 'monotone' | 'step';\n pointradius?: number;\n showpoints?: boolean;\n strokewidth?: number;\n yaxis?: 'left' | 'right';\n}\n\nexport interface linechartconfig extends basechartconfig {\n crosshair?: crosshairconfig | boolean;\n rightyaxis?: omit<yaxisconfig, 'position' | 'grid'>;\n series: lineseriesconfig[];\n}\n\nexport interface areaseriesconfig extends series<continuousdatum> {\n curve?: 'linear' | 'monotone' | 'step';\n fill?: 'gradient' | 'solid';\n fillopacity?: number;\n showline?: boolean;\n}\n\nexport interface areachartconfig extends basechartconfig {\n crosshair?: crosshairconfig | boolean;\n series: areaseriesconfig[];\n}\n\nexport interface scatterseriesconfig extends series<continuousdatum> {\n pointradius?: number;\n}\n\nexport interface scatterchartconfig extends basechartconfig {\n crosshair?: crosshairconfig | boolean;\n series: scatterseriesconfig[];\n}\n\nexport interface barseriesconfig extends series {\n borderradius?: number;\n}\n\nexport type barvariant = 'grouped' | 'grouped horizontal' | 'stacked' | 'stacked horizontal';\n\nexport interface barchartconfig extends basechartconfig {\n series: barseriesconfig[];\n variant?: barvariant;\n}\n\nexport type pievariant = 'donut' | 'pie' | 'semi';\n\nexport interface piesliceconfig {\n color?: string;\n label?: string;\n value: number;\n}\n\nexport interface pieevent {\n index: number;\n originalevent: event;\n slice: piesliceconfig;\n}\n\nexport interface piechartconfig extends omit<basechartconfig, 'margin' | 'onclick' | 'onhover' | 'xaxis' | 'yaxis'> {\n cornerradius?: number;\n data: piesliceconfig[];\n innerradius?: number;\n onclick?: (event: pieevent) => void;\n onhover?: (event: pieevent | null) => void;\n padpixels?: number;\n variant?: pievariant;\n}\n\nexport interface radaraxisconfig {\n format?: (value: number) => string;\n key: string;\n label: string;\n max?: number;\n min?: number;\n}\n\nexport type radarseriesconfig = series<datum<string>>;\n\nexport interface radargridconfig {\n bands?: boolean;\n labels?: boolean;\n levels?: number;\n shape?: 'circle' | 'polygon';\n}\n\nexport interface radaraxisvalue {\n datum: datum<string> | undefined;\n series: radarseriesconfig;\n}\n\nexport interface radarevent {\n axis: radaraxisconfig;\n index: number;\n originalevent: event;\n values: radaraxisvalue[];\n}\n\nexport interface radarchartconfig extends omit<basechartconfig, 'margin' | 'onclick' | 'onhover' | 'xaxis' | 'yaxis'> {\n axes: radaraxisconfig[];\n curve?: 'linear' | 'rounded';\n domain?: [number, number];\n fill?: 'gradient' | 'none' | 'solid';\n grid?: radargridconfig | boolean;\n onclick?: (event: radarevent) => void;\n onhover?: (event: radarevent | null) => void;\n series: radarseriesconfig[];\n showpoints?: boolean;\n showvalues?: boolean;\n startangle?: number;\n}\n\nexport type sparklinevariant = 'area' | 'bar' | 'line' | 'stack';\n\nexport interface stacksegment {\n color?: string;\n label?: string;\n value: number;\n}\n\nexport interface sparklineevent {\n index: number;\n originalevent: event;\n value: number;\n}\n\nexport interface sparklineconfig {\n a11y?: charta11y;\n color?: string;\n cornerradius?: number;\n curve?: 'linear' | 'monotone' | 'step';\n data: number[] | stacksegment[];\n fillopacity?: number;\n onclick?: (event: sparklineevent) => void;\n onhover?: (event: sparklineevent | null) => void;\n padpixels?: number;\n showendpoint?: boolean;\n strokewidth?: number;\n transition?: transitionoption;\n variant?: sparklinevariant;\n}\n```\n\n## errors\n\n### `prismerror`\n\n```ts\nclass prismerror extends error {\n constructor(message: string, opts?: erroroptions);\n}\n```\n\nbase class for prism originated errors. its `name` is set to the concrete error class name, and it supports the standard `erroroptions` including `cause`.\n\n### `prismrendererror`\n\n```ts\nclass prismrendererror extends prismerror {}\n```\n\nthrown when a chart cannot be rendered because its container is not a dom element, when initial rendering fails (the original error is available as `cause`), or when `update()` is called after disposal. empty or malformed chart data may instead produce a development warning or an empty rendering, depending on the chart.\n",
943
+ "usage": " \ntitle: 'prism: usage guide'\ndescription: create responsive svg charts, update their data, handle interaction, and theme @vielzeug/prism.\n \n\n[[toc]]\n\n## basic usage\n\ncreate a host element with non zero dimensions, then pass it to a chart factory. this example creates the host in javascript so the snippet can run as written.\n\n```ts\nimport { createlinechart } from '@vielzeug/prism';\nimport '@vielzeug/prism/theme.css';\n\nconst container = document.createelement('div');\ncontainer.style.csstext = 'width:640px;height:320px';\ndocument.body.append(container);\n\nconst chart = createlinechart(container, {\n a11y: { arialabel: 'revenue by month' },\n series: [\n {\n name: 'revenue',\n data: [\n { key: 1, value: 10 },\n { key: 2, value: 16 },\n ],\n },\n ],\n tooltip: true,\n});\n\nchart.dispose();\n```\n\neach factory appends an svg to its container and returns a `charthandle`. prism observes the container with `resizeobserver`; if it has zero dimensions on creation, prism uses a temporary `600 × 300` size and warns in development. give the host its layout before creating the chart.\n\n## updating data\n\nkeep application state outside prism and pass the next complete data value to `update()`. the update method replaces chart data synchronously; the default transition may animate the resulting svg.\n\n```ts\nimport { createlinechart } from '@vielzeug/prism';\n\nconst container = document.createelement('div');\ncontainer.style.csstext = 'width:640px;height:320px';\ndocument.body.append(container);\n\nlet series = [{ data: [{ key: 1, value: 10 }], name: 'live' }];\nconst chart = createlinechart(container, { series });\n\nseries = [{ data: [{ key: 1, value: 10 }, { key: 2, value: 20 }], name: 'live' }];\nchart.update(series);\nchart.dispose();\n```\n\n`update()` accepts the type shown by that factory's handle: a complete series array for line, area, scatter, bar, and radar charts; a slice array for pie charts; and a number or stack segment array for sparklines. chart options such as axes, variant, callbacks, and accessibility are set at creation. for example, radar axes and a pie chart's variant do not change through `update()`.\n\n## line charts\n\nline and area series use number or `date` keys. all series in one chart must use the same key kind. use `curve` to choose interpolation, and enable markers with `showpoints`.\n\n```ts\nimport { createlinechart } from '@vielzeug/prism';\n\nconst container = document.createelement('div');\ncontainer.style.csstext = 'width:640px;height:320px';\ndocument.body.append(container);\n\nconst chart = createlinechart(container, {\n a11y: { arialabel: 'revenue and expenses' },\n series: [\n {\n curve: 'monotone',\n data: [{ key: 1, value: 100 }, { key: 2, value: 150 }, { key: 3, value: 130 }],\n name: 'revenue',\n showpoints: true,\n },\n {\n color: '#c2410c',\n data: [{ key: 1, value: 70 }, { key: 2, value: 95 }, { key: 3, value: 110 }],\n name: 'expenses',\n },\n ],\n tooltip: true,\n xaxis: { label: 'month' },\n yaxis: { grid: true, label: 'dollars' },\n});\n\nchart.dispose();\n```\n\n### independent value axes\n\nassign a line series to the right axis when it uses a different value range. both axes share the x scale; `rightyaxis` configures the second axis and cannot configure its position or grid.\n\n```ts\nimport { createlinechart } from '@vielzeug/prism';\n\nconst container = document.createelement('div');\ncontainer.style.csstext = 'width:640px;height:320px';\ndocument.body.append(container);\n\nconst chart = createlinechart(container, {\n a11y: { arialabel: 'health and damage by level' },\n rightyaxis: { label: 'damage' },\n series: [\n { data: [{ key: 1, value: 30 }, { key: 2, value: 20 }], name: 'health' },\n { data: [{ key: 1, value: 100 }, { key: 2, value: 300 }], name: 'damage', yaxis: 'right' },\n ],\n yaxis: { grid: true, label: 'health' },\n});\n\nchart.dispose();\n```\n\nwhen any series uses the right axis, the primary value axis is placed on the left. grid lines belong to the primary `yaxis`. the independent numeric scales mean that curve crossings do not imply equal values.\n\n### time based x axis\n\nuse `date` values for every key when plotting time. line and area charts use a time scale whose domain fits the data instead of rounding outward to tick boundaries.\n\n```ts\nimport { createlinechart } from '@vielzeug/prism';\n\nconst container = document.createelement('div');\ncontainer.style.csstext = 'width:640px;height:320px';\ndocument.body.append(container);\n\nconst formatter = new intl.datetimeformat(undefined, { month: 'short', day: 'numeric' });\nconst chart = createlinechart(container, {\n a11y: { arialabel: 'signups over time' },\n series: [{\n data: [\n { key: new date('2026 09 01'), value: 50 },\n { key: new date('2026 09 08'), value: 80 },\n { key: new date('2026 09 15'), value: 120 },\n ],\n name: 'signups',\n }],\n xaxis: { tickformat: (value) => value instanceof date ? formatter.format(value) : string(value) },\n});\n\nchart.dispose();\n```\n\n`tickformat` formats axis labels only. tooltip content and accessibility announcements use the underlying keys and values.\n\n## bar charts\n\nbar chart keys are string categories. the default `grouped` variant puts series side by side; the horizontal variants put categories on the y axis and values on the x axis.\n\n```ts\nimport { createbarchart } from '@vielzeug/prism';\n\nconst container = document.createelement('div');\ncontainer.style.csstext = 'width:640px;height:320px';\ndocument.body.append(container);\n\nconst chart = createbarchart(container, {\n a11y: { arialabel: 'sales by quarter' },\n series: [{\n data: [{ key: 'q1', value: 200 }, { key: 'q2', value: 350 }, { key: 'q3', value: 280 }],\n name: 'sales',\n }],\n tooltip: true,\n});\n\nchart.dispose();\n```\n\nchoose `grouped`, `stacked`, `grouped horizontal`, or `stacked horizontal` in `variant`. negative values in a stacked chart are clamped to zero and produce a development warning. bars in stacked variants remain square; `borderradius` applies to grouped bars.\n\naxes render by default, with grid lines on the value axis. for horizontal variants, `xaxis` is the value axis and `yaxis` is the category axis. configure `grid` on the value axis.\n\n## area charts\n\narea series use a gradient fill by default. set `fill: 'solid'` for a flat fill, or set `fillopacity` to override the theme opacity for that series.\n\n```ts\nimport { createareachart } from '@vielzeug/prism';\n\nconst container = document.createelement('div');\ncontainer.style.csstext = 'width:640px;height:320px';\ndocument.body.append(container);\n\nconst chart = createareachart(container, {\n a11y: { arialabel: 'visitors over time' },\n crosshair: true,\n series: [{\n curve: 'monotone',\n data: [{ key: 1, value: 12 }, { key: 2, value: 18 }, { key: 3, value: 15 }],\n fill: 'solid',\n fillopacity: 0.2,\n name: 'visitors',\n showline: true,\n }],\n});\n\nchart.dispose();\n```\n\n## scatter charts\n\nscatter charts treat every datum as an independent point: `key` is the numeric x value and `value` is the y value. the scales fit the observed values rather than forcing the y domain to include zero. set `pointradius` on a series to change its marker size.\n\n```ts\nimport { createscatterchart } from '@vielzeug/prism';\n\nconst container = document.createelement('div');\ncontainer.style.csstext = 'width:640px;height:320px';\ndocument.body.append(container);\n\nconst chart = createscatterchart(container, {\n a11y: { arialabel: 'height against weight' },\n series: [{\n data: [{ key: 160, value: 55 }, { key: 175, value: 70 }, { key: 190, value: 88 }],\n name: 'cohort a',\n pointradius: 5,\n }],\n tooltip: true,\n});\n\nchart.dispose();\n```\n\nscatter callbacks identify one nearest point and do not provide cross series `values`, because points do not share a category key.\n\n## forecast and emphasis styling\n\nadd `dash` or `opacity` to individual `datum` objects when one part of a series needs distinct styling. these fields do not change interaction, tooltip, or accessibility behavior.\n\n```ts\nimport { createlinechart } from '@vielzeug/prism';\n\nconst container = document.createelement('div');\ncontainer.style.csstext = 'width:640px;height:320px';\ndocument.body.append(container);\n\nconst values = [10, 13, 15, 18];\nconst chart = createlinechart(container, {\n series: [{\n data: values.map((value, index) => ({\n key: index + 1,\n value,\n ...(index >= 2 ? { dash: '5 5', opacity: 0.4 } : {}),\n })),\n name: 'forecast',\n showpoints: true,\n }],\n});\n\nchart.dispose();\n```\n\nfor line and area charts, each datum's `dash` styles the segment from that datum to the next; the final datum has no outgoing segment. line opacity also fades the datum's marker. area styling affects the top line, not the fill. bar opacity and dashes apply to the individual bar; scatter opacity applies to its point and scatter dashes are ignored. pie, radar, and sparkline ignore these two fields.\n\n## pie, donut, and semi charts\n\nuse `createpiechart()` for all three shapes. `variant` is fixed when the chart is created; `update()` replaces the slice data.\n\n```ts\nimport { createpiechart } from '@vielzeug/prism';\n\nconst container = document.createelement('div');\ncontainer.style.csstext = 'width:320px;height:240px';\ndocument.body.append(container);\n\nconst chart = createpiechart(container, {\n a11y: { arialabel: 'orders by channel' },\n data: [\n { label: 'direct', value: 42 },\n { label: 'organic', value: 28 },\n { label: 'referral', value: 18 },\n { label: 'social', value: 12 },\n ],\n legend: true,\n tooltip: true,\n variant: 'donut',\n});\n\nchart.dispose();\n```\n\nthe `pie` default has no inner radius. `donut` and `semi` default to an inner radius of 55% of the outer radius. `innerradius` overrides that size in pixels. slice labels are omitted when they do not fit; keep a tooltip or legend when small slices need identifying.\n\n## radar charts\n\ndefine at least three axes when creating a radar chart. series data uses string keys that match `radaraxisconfig.key`; `update()` replaces series but keeps axes and other configuration fixed.\n\n```ts\nimport { createradarchart } from '@vielzeug/prism';\n\nconst container = document.createelement('div');\ncontainer.style.csstext = 'width:400px;height:320px';\ndocument.body.append(container);\n\nconst axes = [\n { key: 'strength', label: 'strength' },\n { key: 'agility', label: 'agility' },\n { key: 'endurance', label: 'endurance' },\n];\nconst chart = createradarchart(container, {\n a11y: { arialabel: 'hero stats' },\n axes,\n domain: [0, 10],\n series: [{\n data: [{ key: 'strength', value: 8 }, { key: 'agility', value: 6 }, { key: 'endurance', value: 9 }],\n name: 'adam',\n }],\n tooltip: true,\n});\n\nchart.dispose();\n```\n\nthe default shared domain starts at zero and uses a nice maximum; set `domain` to compare series on a fixed range. per axis `min` and `max` override the chart domain for that axis. grid rings, bands, curves, fills, vertex dots, and values are configurable; see `radarchartconfig` and `radargridconfig` in the api reference.\n\n## sparklines\n\nsparklines omit axes, margins, and legends. choose `line`, `area`, or `bar` for numeric arrays, and `stack` for an array of `stacksegment` objects.\n\n```ts\nimport { createsparkline } from '@vielzeug/prism';\n\nconst container = document.createelement('div');\ncontainer.style.csstext = 'width:240px;height:48px';\ndocument.body.append(container);\n\nconst chart = createsparkline(container, {\n a11y: { arialabel: 'weekly orders' },\n data: [10, 14, 12, 18, 22],\n variant: 'area',\n});\n\nchart.update([10, 14, 12, 18, 22, 25]);\nchart.dispose();\n```\n\nfor a stacked sparkline, each segment can set `value`, `color`, and `label`. sparklines do not offer keyboard navigation, a tooltip, or a live value announcer. add a visible value or summary when the trend conveys important information.\n\n## interaction\n\nset `tooltip: true` to show prism's default tooltip, or pass a `tooltipconfig` to format its content. a `render` callback receives the active `datum` and `series`. returned strings are inserted as text; return a dom `node` for structured content.\n\n```ts\nimport { createlinechart } from '@vielzeug/prism';\n\nconst container = document.createelement('div');\ncontainer.style.csstext = 'width:640px;height:320px';\ndocument.body.append(container);\n\nconst chart = createlinechart(container, {\n series: [{ data: [{ key: 1, value: 4200 }, { key: 2, value: 5100 }], name: 'revenue' }],\n tooltip: {\n render: (datum, series) => `${series.name}: ${new intl.numberformat().format(datum.value)}`,\n },\n});\n\nchart.dispose();\n```\n\n`onhover` receives an event while a point, category, slice, or radar axis is active, then `null` when the interaction clears. `onclick` receives the selected event. for line, area, and bar charts, `chartevent.values` contains each series' datum at that shared key; a missing datum is `undefined`. scatter, pie, radar, and sparkline use their chart specific event types.\n\n`crosshair` is available on line, area, and scatter charts. by default it shows a vertical guide and snaps the line to the nearest datum. set `horizontal: true` to add a horizontal guide or `vertical: false` to hide the vertical one. set `snap: false` to make the guide follow the pointer; tooltip and callback data still come from the nearest datum.\n\n### keyboard and announcements\n\nset `a11y: { arialabel }` on an informative chart. prism gives the svg `role=\"img\"`, an accessible name, and keyboard focus. arrow keys move among chart data; `home` and `end` move to the first and last item where supported; `enter` or `space` activates an item when an `onclick` callback is configured; `escape` clears the active selection. sparklines do not support keyboard navigation.\n\nwhen `a11y` is omitted, or set to `{ decorative: true }`, the svg is marked `aria hidden=\"true\"` and does not enter the keyboard tab order. only mark charts decorative when their information is available elsewhere. interactive cartesian and pie/radar charts use the tooltip as a polite live region when enabled; otherwise they announce active values in `.prism live`. sparklines have no live value announcements, so provide an equivalent visible value or summary.\n\n## animation\n\nchart motion is enabled by default, with a 300 ms duration per animated lane (radar uses 400 ms). pass `transition: false` to render updates synchronously, or pass `true` to state the default explicitly.\n\n```ts\nimport { createbarchart } from '@vielzeug/prism';\n\nconst container = document.createelement('div');\ncontainer.style.csstext = 'width:640px;height:320px';\ndocument.body.append(container);\n\nconst chart = createbarchart(container, {\n series: [{ data: [{ key: 'a', value: 4 }, { key: 'b', value: 7 }], name: 'count' }],\n transition: { duration: 500, easing: 'ease out', preference: 'system' },\n});\n\nchart.dispose();\n```\n\ncolumns grow together from their baseline by default over 300 ms. lines trace their final curve over 420 ms, uncovering point markers along the way without fading or shifting the series. line data updates retain the default 300 ms geometry transition; an explicit `duration` overrides both timings.\n\n`preference: 'system'` (the default) disables javascript motion when the user prefers reduced motion. use `'always'` to ignore that preference or `'never'` to disable motion. `stagger` sets an optional delay between bars or radar series; bar charts cap the effective delay so long category axes do not add unbounded time. named easing values are `ease in`, `ease in out`, `ease out`, `back out`, `expo out`, and `linear`; you can also provide an easing function.\n\n## responsive layout and lifecycle\n\nsize the host with css. prism observes its content box and updates the svg dimensions and chart layout when it changes. bar and line resize redraws apply immediately rather than animating between layout sizes; entrance animations and explicit data update transitions remain enabled. the legend is rendered as container chrome outside the svg and included in resize calculations.\n\ncall `dispose()` when the view no longer owns the chart. disposal is idempotent: it removes the svg, tooltip, legend, and resize observation, stops in flight chart motion, and aborts `disposalsignal`. `chart[symbol.dispose]()` calls the same cleanup method.\n\nuse `tap()` when external code needs resize or dispose notifications rather than observing the chart dom itself:\n\n```ts\nimport { createlinechart } from '@vielzeug/prism';\n\nconst container = document.createelement('div');\ncontainer.style.csstext = 'width:640px;height:320px';\ndocument.body.append(container);\n\nconst chart = createlinechart(container, {\n series: [{ data: [{ key: 1, value: 10 }], name: 'revenue' }],\n});\nconst stopobserving = chart.tap((event) => {\n if (event.type === 'resize') console.log(event.width, event.height);\n if (event.type === 'dispose') console.log('chart disposed');\n});\n\nstopobserving();\nchart.dispose();\n```\n\n`tap()` returns an unsubscribe function and accepts `{ signal }` for automatic unsubscription. a listener exception is swallowed by the observer so it cannot interrupt chart rendering.\n\n## theming\n\nimport the stylesheet once to load prism's structural styles, default tokens, dark mode rules, and reduced motion css. you can override any css custom property on an ancestor of the chart.\n\n```css\n.dashboard chart {\n prism color 1: #2563eb;\n prism color 2: #16a34a;\n prism grid color: #94a3b8;\n prism line width: 3;\n prism text color: #172033;\n prism tooltip bg: #111827;\n}\n```\n\n`settheme()` provides a typed subset of theme properties. a scoped call writes inline values on the provided element; omit `scope` to write them to `document.documentelement`. inline values override stylesheet tokens until reset.\n\n```ts\nimport { resettheme, settheme } from '@vielzeug/prism';\n\nconst container = document.createelement('div');\ncontainer.style.csstext = 'width:640px;height:320px';\ndocument.body.append(container);\n\nsettheme({ colors: ['#2563eb', '#16a34a'], fontfamily: 'inter, sans serif' }, { scope: container });\n// call resettheme({ scope: container }) when this scoped theme is no longer needed.\nresettheme({ scope: container });\n```\n\n### available tokens\n\nthe stylesheet defines defaults for the following token groups. pie label color and size are read with fallback values by the renderer but are not declared in the base token block. set a token on the chart container or an ancestor to scope an override.\n\n| group | css custom properties |\n| | |\n| palette and surfaces | ` prism color 1` through ` prism color 8`, ` prism bg`, ` prism border radius` |\n| axes and grid | ` prism axis color`, ` prism axis width`, ` prism grid color`, ` prism grid width`, ` prism grid dash`, ` prism grid opacity` |\n| text | ` prism font family`, ` prism font size`, ` prism font size label`, ` prism font size title`, ` prism font weight`, ` prism font weight title`, ` prism text color`, ` prism text color secondary` |\n| tooltip | ` prism tooltip bg`, ` prism tooltip color`, ` prism tooltip border`, ` prism tooltip radius`, ` prism tooltip padding`, ` prism tooltip shadow`, ` prism tooltip font size` |\n| crosshair | ` prism crosshair color`, ` prism crosshair width`, ` prism crosshair dash`, ` prism crosshair opacity` |\n| points | ` prism point radius`, ` prism point radius hover`, ` prism point stroke width`, ` prism point stroke` |\n| bars | ` prism bar radius`, ` prism bar hover opacity`, ` prism bar gap`, ` prism bar band fill`, ` prism bar band opacity`, ` prism bar dim opacity` |\n| areas and lines | ` prism area opacity`, ` prism area stroke width`, ` prism area gradient start`, ` prism area gradient end`, ` prism line width`, ` prism line width hover` |\n| interaction | ` prism dim opacity`, ` prism active point radius`, ` prism point ring`, ` prism focus color` |\n| sparklines | ` prism spark fill opacity`, ` prism spark gradient start`, ` prism spark gradient end` |\n| radar | ` prism radar grid color`, ` prism radar grid opacity`, ` prism radar band fill`, ` prism radar band opacity`, ` prism radar spoke color`, ` prism radar fill opacity`, ` prism radar stroke width`, ` prism radar point radius`, ` prism radar point radius active`, ` prism radar label color`, ` prism radar label size`, ` prism radar value color`, ` prism radar value size`, ` prism radar dim opacity` |\n| motion | ` prism duration fast`, ` prism duration`, ` prism duration slow`, ` prism ease`, ` prism ease spring`, ` prism transition`, ` prism transition fast` |\n| legend and pie labels | ` prism legend gap`, ` prism legend dot size`, ` prism legend font size`, ` prism pie label color`, ` prism pie label size` |\n\nthe default palette and structural colors adapt to `prefers color scheme: dark`, `html.dark`, or `[data prism theme='dark']` on an ancestor. scoped inline values set with `settheme()` take precedence in either mode. the stylesheet also provides `prefers reduced motion` overrides for css transitions.\n\n## errors and diagnostics\n\nprism throws `prismrendererror` when the container is not a dom element, when initial rendering fails, or when `update()` is called on a disposed chart. a wrapped initial render error exposes its cause. use `prismerror` to catch all prism originated errors.\n\nempty or malformed data is not always an exception. depending on the chart, prism may draw nothing or emit a development only `console.warn`; examples include an unknown `bandscale` category, negative values in stacked bars, and radar axes with fewer than three entries or duplicate keys. correct the input rather than relying on a warning for validation.\n\n## framework integration\n\nprism has no react, vue, or svelte adapter. in any framework, give prism a host element after it is mounted, call `update()` when data changes, and call `dispose()` before the host is removed. this plain dom pattern shows the ownership boundary without depending on a framework specific lifecycle api:\n\n```ts\nimport { createlinechart, type lineseriesconfig } from '@vielzeug/prism';\n\nfunction mountchart(host: htmlelement, series: lineseriesconfig[]) {\n const chart = createlinechart(host, { a11y: { arialabel: 'revenue' }, series });\n\n return {\n update(next: lineseriesconfig[]) {\n chart.update(next);\n },\n dispose() {\n chart.dispose();\n },\n };\n}\n\nconst host = document.createelement('div');\nhost.style.csstext = 'width:640px;height:320px';\ndocument.body.append(host);\nconst view = mountchart(host, [{ data: [{ key: 1, value: 10 }], name: 'revenue' }]);\nview.update([{ data: [{ key: 1, value: 10 }, { key: 2, value: 15 }], name: 'revenue' }]);\nview.dispose();\n```\n\nthe application owns its state and host; prism owns only the svg, observer, and optional overlay elements it creates.\n\n## working with other vielzeug libraries\n\nuse refine components for dashboard controls and keep prism in a sized host element. map refine theme tokens to prism css custom properties or call `settheme()` with a scoped element; prism does not require a refine adapter. prism uses orbit internally to place tooltips, so chart users do not need to create orbit floating elements for the built in tooltip.\n\n## best practices\n\n give the host a non zero size before creating a chart.\n set `a11y.arialabel` when a chart communicates information.\n use `date` or number keys consistently across each line or area chart.\n pass the complete new data value to `update()`.\n keep chart options fixed after creation; recreate the chart to change them.\n dispose the handle when its host leaves the owning view.\n use `transition: false` for deterministic synchronous rendering.\n prefer css custom properties for reusable theme overrides.\n",
944
+ "examples": " \ntitle: 'prism: examples'\ndescription: focused recipes for creating, updating, interacting with, and theming prism charts.\n \n\n [create a line chart](./examples/line chart.md)\n [plot a time series](./examples/time series.md)\n [plot a scatter chart](./examples/scatter chart.md)\n [compare categories with a bar chart](./examples/bar chart.md)\n [render compact charts](./examples/compact charts.md)\n [compare hero stats with a radar chart](./examples/radar chart.md)\n [update chart data](./examples/update data.md)\n [sync a details panel with hover](./examples/hover details.md)\n [render a custom tooltip](./examples/custom tooltip.md)\n [match charts to your design system](./examples/theming.md)\n"
945
945
  },
946
946
  "examples": [],
947
- "exports": "createlinechart createbarchart createareachart createpiechart createradarchart createsparkline settheme prismerror",
948
- "keywords": "chart svg visualization responsive line chart bar chart area chart pie chart radar chart sparkline accessibility typescript",
947
+ "exports": "createlinechart createbarchart createpiechart charthandle",
948
+ "keywords": "chart svg visualization responsive line chart bar chart area chart scatter chart pie chart radar chart sparkline accessibility typescript",
949
949
  "name": "@vielzeug/prism",
950
950
  "related": "refine orbit",
951
951
  "slug": "prism",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@vielzeug/codex",
3
- "version": "26.10.4",
3
+ "version": "26.10.5",
4
4
  "description": "MCP server exposing all Vielzeug docs to AI clients",
5
5
  "repository": {
6
6
  "type": "git",
@@ -62,7 +62,7 @@
62
62
  },
63
63
  "devDependencies": {
64
64
  "@types/node": "^26.6.2",
65
- "@vielzeug/refine": "26.10.4",
65
+ "@vielzeug/refine": "26.10.5",
66
66
  "typescript": "^6.0.3",
67
67
  "vitest": "^5.0.1"
68
68
  }