Alpha — breaking changes ship without a deprecation cycle. What is built vs. planned
Apache ECharts exporter
Acme · Cathode · GOV.UK · Carbon: one page, four design systems, compiled to Apache ECharts. All 44 demos →
This is the second reference exporter, and it shows Transtyle isn’t a CSS generator. The output is a JSON theme object for echarts.registerTheme(), and colors are hex (canvas rendering). The star is a derivation problem no UI-framework target has: the categorical data palette.
It’s also the smallest surface any target exposes, with 19 classified theme keys on Acme. That’s why the palette is the whole story here.
The palette problem#
ECharts’ most important themable value is color: [...], which holds 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. It’s the single-source-of-truth promise applied where hand-maintained themes always drift. Authored palettes win as always: author semantic.palette.categorical.* tokens to pin your own.
Pin your own and the compiler checks them: two entries closer than ΔEOK 0.05 in a mode raise TST2102, because series that close can’t be told apart on a chart. The derived palette never does: its closest pair is 0.082.
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[0] … color[7] |
palette.categorical.1–8, one each |
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 at units.remBase (16 by default), classified approximated |
| series-specific styles (candlestick, gauge…) | — | 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, and it’s fun to build just to look at.
Descriptions and deprecations#
Only the .js twin takes comments. In theme.<name>.js, a property that maps exactly one slot (the background, the text and border colours, the tooltip surface and radius, the font family) carries that slot’s description and deprecated as a /** … */ JSDoc block above it (Documented and jsdocComment() from @transtyle/ir). @deprecated <reason> marks a deprecated slot. The color palette is an array of eight slots and gets none. The .json file holds the same theme and, being JSON, no comments.
Contrast, motion and brand#
brand multiplies the themes the way color-scheme does: one theme per brand and scheme, named <project>-<brand>-<scheme> (acme-globex-dark), every brand named, the default one included. contrast and motion are reported dropped: a theme is chosen at init with no media query, and animation duration is a chart option.