How the charts became part of the system.
Most design systems stop at the component boundary and leave charts to whatever library each team reaches for — which is exactly where a system drifts into a second palette, a second typeface, a second set of conventions. Vael treats charts as first-class system components. Here is how.
Theme the engine, don’t fork it.
The tempting move is to build a bespoke charting layer so it matches the system perfectly. It’s also the wrong move — you inherit years of solved edge cases (axis math, stacking, accessibility, export, timezones). So the call was to adopt a mature engine — Highcharts — and theme it completely, never fork it, never hand-roll SVG. It earns its place for three reasons: styled mode (the whole look is driven from the outside by tokens, not a config full of hex), accessibility built in (screen-reader descriptions and keyboard nav that are hard to retrofit), and breadth without new abstractions (line, area, bar, donut, box, scatter — one engine, no parallel vocabulary). The judgment — use a mature engine and own the theming — is the interesting part. The wiring is just the execution of it.
Global defaults, applied at import
A single Highcharts.setOptions runs at module-import time — before any chart mounts, because Highcharts renders synchronously and a useEffect would fire too late. It sets what's true for every chart: styled mode, a transparent surface, the categorical palette drawn from the system's ramps, IBM Plex Mono labels, accessibility on, and no chrome.
A per-render theme merge
Global defaults are static; the theme is not — it flips light and dark. So each chart merges a live, theme-aware options object on every render. buildVcThemeOptions(theme) reads the current MUI theme and returns axis colors, grid lines, tooltip ink, and labels derived from the palette. Because it runs per render, the charts switch light/dark together with every other component — no chart-specific toggle, no duplicated palette.
A CSS-variable bridge for the SVG
Some of a chart isn't reachable from a config object — the parts Highcharts paints as SVG classes in styled mode, plus the tooltip, which renders in a portal outside the chart container. Those read from a --vc-* custom-property map injected on the container, so even the parts outside the React tree pull from the same tokens.
Owning the theme also meant owning the constraints — enforced in code, not left to a doc:
- Styled mode only — no default Highcharts theme, no inline hardcoded colors.
- At most 4 visible series and 2 y-axes by default — the ceiling where pre-attentive processing still works and axis assignment stays unambiguous.
- Line style carries meaning alongside color — actual (solid), threshold (dashed), predicted (dotted) — so a chart survives print, screenshots, and colorblind readers.
- Visible gaps — no interpolation across missing data.
One toggle themes everything — charts and UI switch light/dark from the same source, so a chart can’t fall out of sync. There’s no parallel design language: chart color, type, and ink are the product’s tokens, not a second palette maintained by hand. Accessibility comes for free on every chart because it’s set once at the engine level. And new chart types are new presets over a themed engine — not new code to keep on-brand.