Chart Customization
Every built-in native chart uses the same renderer customization contract. This applies to all 68 native types, whether created through a per-type factory, the full catalog, or Pivot. Use chart state for saved settings and renderer options for application callbacks and branding.
Choose the right control
Section titled “Choose the right control”| Detail | API |
|---|---|
| Default series/category colors | rendererOptions.palette |
| Stable semantic colors, member/measure colors, gradients, conditional colors | colors |
| Series color, visibility, axis, mark type, stacking, errors | seriesOverrides[seriesId] (supported fields depend on the chart family) |
| Chart background, foreground, muted text, grid | rendererOptions.theme (tokens or a callback) |
| Titles, description, legend placement, data labels | presentation |
| Every SVG label’s content, visibility, typography, class, accessible name | rendererOptions.labels.styles and labels.formatter |
| Individual marks, fills, outlines, dashes, opacity and typography | rendererOptions.elements.styles and elements.formatter |
| Tooltips and their content | rendererOptions.tooltip, including formatter, className and timing |
| Domains, scales, tick formats, units, titles | axes |
| Reference lines, ranges, text and drawing shapes | annotations |
| Family-specific geometry | chartOptions; inspect the selected catalog definition’s schema |
| Arbitrary SVG geometry, custom layers and remaining visual details | rendererOptions.onRender |
| UI strings and number/date formatting | localeText |
Model properties remain subject to the selected chart’s capabilities and validation. For example, axis-free charts have no numeric axis to configure. Renderer style callbacks provide visual overrides across all native types; they do not add new analytical capabilities.
Per-chart factories
Section titled “Per-chart factories”import { createLineChart } from '@revolist/charts/line';
const chart = await createLineChart({ container, data: [{ month: 'Jan', revenue: 120 }, { month: 'Feb', revenue: 180 }], mapping: { category: { field: 'month' }, series: [{ field: 'revenue', label: 'Revenue' }], }, presentation: { title: 'Monthly revenue', dataLabels: true }, rendererOptions: { palette: ['#6647f0', '#fd7e14'], fontFamily: 'Inter, system-ui, sans-serif', theme: { background: '#ffffff', foreground: '#172033', grid: '#e2e8f0' }, labels: { styles: { title: { fontSize: 20, fontWeight: 700 }, data: { fill: '#172033', fontSize: 12 }, }, formatter(context) { if (context.kind === 'data' && context.value != null) { return { text: `€${context.value}`, ariaLabel: `${context.value} euros` }; } }, }, elements: { styles: { mark: { strokeWidth: 2, strokeLinejoin: 'round' } }, formatter(context) { if (context.kind === 'mark' && context.value != null && context.value > 150) { return { fill: '#0f766e', stroke: '#115e59' }; } }, }, onRender({ svg, bounds }) { const note = svg.ownerDocument.createElementNS('http://www.w3.org/2000/svg', 'text'); note.setAttribute('x', String(bounds.left + bounds.width)); note.setAttribute('y', '16'); note.setAttribute('text-anchor', 'end'); note.setAttribute('fill', '#64748b'); note.setAttribute('font-size', '10'); note.textContent = 'Internal reporting'; svg.append(note); }, },});rendererOptions configures the factory’s native renderer. Supply either it or an explicit renderer; combining both rejects before rendering. A custom renderer is responsible for its own customization contract. A palette colors the family-defined series or category channel; use colors when identities must remain stable across ordering changes.
For a small custom native renderer bundle, the equivalent is createNativeChartsRenderer([lineChart], options) from @revolist/charts/core. For Pivot, pass the same options to createPivotChartsRenderer(options) from @revolist/pivot/charts and install that renderer in the chart plugin configuration.
Semantic labels
Section titled “Semantic labels”labels.styles keys are title, subtitle, axisTitle, axisTick, category, legend, data, annotation, summary and specialized. labels.formatter receives chartId, chartType, renderMode, kind, defaultText, and available axis/category/series/value/annotation metadata. Return a string to replace text, a ChartLabelResult to set text/style/classes/accessibility, null or { hidden: true } to suppress a label, or undefined to keep it. Use this API for typography so native heading and label layout can measure the authored text.
Visual elements and precedence
Section titled “Visual elements and precedence”elements.styles keys are:
background: the chart’s background rectangle.mark: datum, inspection or mark-group graphics, including grouped decorative children.label: all SVG text.labelKindidentifies its semantic label role.legend: non-text graphics within legend items or overflow controls.annotation: non-text graphics with an annotation identity.decoration: remaining visible SVG graphics, such as grids, axes, paths and navigation graphics.
elements.formatter receives the chart ID/type, render mode, role, SVG tagName, original attributes, and available canonical categoryIndex, seriesIndex, category, series and value. Series identities remain canonical when hidden/helper series are omitted from geometry. Aggregate or decorative visuals may have no individual datum. Use attributes to target a chart-specific graphic that shares a role, such as a line path or a gauge track. The original attributes describe the built-in result before element styling, including label customization.
Styles support fill, stroke, fill/stroke opacity, stroke width/dashes/offset/caps/joins, fill rule, vector effect, paint order, overall opacity and the ChartLabelStyle typography properties. Shared kind styles apply first; the element formatter wins for properties it supplies. Element styles override built-in presentation, chart options and label styles; onRender runs last. Gradients and patterns authored through the model remain intact unless their fill is explicitly overridden.
SVG definitions, clipping paths, masks, patterns, markers and the plot-bounds measurement rectangle are excluded from element styling. Groups are not styled a second time, avoiding multiplied inherited opacity. To alter definitions or geometry, use onRender.
Lifecycle, updates and exports
Section titled “Lifecycle, updates and exports”Both style callbacks and onRender run synchronously on each complete SVG paint: creation, model/data updates, resize, category scrolling and viewport navigation. Callbacks also run in previews; inspect renderMode when needed. Lightweight peer-hover and status-only updates do not repaint the whole chart.
onRender receives { svg, input, bounds, theme }, with the canonical input including hidden series. Mutate only the supplied chart SVG. Custom SVG children are removed on the next complete paint, so append them again on every invocation. Keep callbacks deterministic, synchronous and free of chart-update calls; treat the input, theme and bounds as read-only. The chart owns its interaction targets and accessibility, so preserve their event handlers, identity attributes and focus semantics when changing geometry.
SVG, PNG and PDF exports use the customized SVG. SVG additions must be self-contained for portable export. Image/PDF encoder limitations still apply. Runtime callbacks and renderer options are not serialized in chart.model: reapply them when restoring a model. To change dynamic branding, close over application settings and trigger an ordinary chart.update({}); callbacks use the latest settings on that repaint. To change renderer-wide configuration objects, create a new renderer/chart.
Discover each chart’s geometry controls
Section titled “Discover each chart’s geometry controls”import { CHART_DEFINITIONS } from '@revolist/charts/catalog';
const definition = CHART_DEFINITIONS.find(({ type }) => type === chart.model.chartType)!;console.table(definition.options.fields);// Each field includes its key, control, default and numeric range or select values.// definition.options.defaults contains the complete defaults for this type.Use those exact keys in chartOptions. The schema is authoritative for that chart’s built-in controls. Common family controls include bar corner radius and padding, line stroke width, area fill opacity, radial angles/radii, histogram bins, financial price styles, gauge domains/segments and facet scale mode. See capability coverage for the family-specific contracts.
Geographic charts
Section titled “Geographic charts”Geographic charts have a separate provider lifecycle. All three types (choropleth, proportionalSymbolMap, geographicHeatmap) accept rendererOptions on the pack.render(input)/instance.update(input) input:
const instance = pack.render({ container, request, localeText, rendererOptions: { theme: { background: '#f8fafc', foreground: '#172033' }, elements: { styles: { mark: { fill: '#0f766e', stroke: '#ffffff', strokeWidth: 1 } }, formatter: ({ kind, value }) => kind === 'mark' && value != null && value < 0 ? { fill: '#b91c1c' } : undefined, }, onRender({ svg, data }) { svg.setAttribute('aria-label', `Map: ${data.projectionId}`); }, },});await instance.ready;Geographic renderer options include className, fontFamily, theme tokens, the shared element styles/formatter and onRender({ svg, input, data, theme }). Marks expose category/series IDs when supplied by the provider. Locale functions format geographic values and accessible labels; onRender can replace visible attribution text or add data labels. The hook runs after a successful provider paint, including updates and provider-owned overlays. Projection, paths and positions remain provider-owned.
The optional ECharts Canvas adapter has a separate supported-feature contract. Native SVG element callbacks do not apply to Canvas; it does not support the full native appearance/annotation/axis contract.