Alpha — experimental. Breaking changes ship without a deprecation cycle, and nothing here carries a stability promise yet.What is built vs. planned →

Apache ECharts exporter

See it live

Acme · Cathode · GOV.UK · Carbon — one page, four design systems, compiled to Apache ECharts. All 32 demos →

The second reference exporter, and proof that Transtyle is not a CSS generator: the output is a JSON theme object for echarts.registerTheme(), colors are hex (canvas rendering), and the star of the show is a derivation problem no UI-framework target has — the categorical data palette.

It is also the smallest surface any target exposes — 11 classified theme keys on Acme — which is why the palette is the whole story here.

The palette problem

ECharts’ most important themable value is color: [...] — series colors that must be mutually distinguishable, not just on-brand. Design systems define roles, not ten distinct hues. Transtyle derives an 8-color palette from your brand: hues rotated in OKLCH around your primary’s anchor, lightness and chroma held in bands tuned for adjacent distinguishability, re-tuned per mode for dark surfaces.

The same palette feeds shadcn’s --chart-1…5 (the first five colors, frozen by contract) — one brand, one data-viz palette, everywhere. This is the single-source-of-truth promise applied exactly where hand-maintained themes always drift. Authored palettes win as always: author semantic.palette.categorical.* tokens to pin your own.

Artifacts

One theme per color-scheme mode — ECharts has no runtime mode concept, so theme-per-init is the native pattern:

File Purpose
theme.<project>-light.json / theme.<project>-dark.json Theme objects for registerTheme
theme.<project>-<mode>.js Self-registering script-tag variant (also exports the theme in CJS)
usage.md Registration snippets, mode-switching pattern, coverage summary
report.json Coverage + provenance, as always

What maps where

Theme path Comes from Note
color[] palette.categorical.1–8 derived from primary (or authored)
backgroundColor background.base
textStyle.color / .fontFamily text.base, font.sans font list joined to a CSS string
title, legend, axisLabel text.base, text-muted.base
axis lines, ticks, split lines border.base applied to all four axis types
tooltip background / border / text overlay.base, border.base, text.base overlay = floating surface, as everywhere
tooltip.borderRadius radius.md rem → px (base 16) — classified approximated
series-specific styles (candlestick, gauge…) honestly reported unsupported; extend at init

OKLCH → hex may clamp colors outside the sRGB gamut; clamped values are classified approximated with a note.

Usage

"targets": { "echarts": { "output": "dist/echarts" } }
import * as echarts from 'echarts';
import light from './theme.acme-design-system-light.json' with { type: 'json' };
echarts.registerTheme('acme-light', light);
const chart = echarts.init(el, 'acme-light');

Mode switching: dispose and re-init with the other theme name (ECharts fixes the theme at init). Both examples ship ECharts targets — Cathode’s dark theme opens with phosphor green on tube-black, which is worth building just to look at.