Files
astrolabe/src/core/vega-themes.ts
T

104 lines
3.2 KiB
TypeScript

/**
* Vega-Lite chart config per UI theme (docs/architecture/05 §3, 09 §6).
*
* Portable core: a Vega-Lite `Config` styles every chart globally so charts
* visually belong to the app rather than looking like stock Vega-Lite. This is
* the single source of truth mapping a `UiTheme` to a config; it is applied at
* embed time (never baked into the user's stored spec). Adding a UI theme = one
* config object plus one map entry here.
*
* Values track the design language: IBM Plex font, axis/grid colors from the
* Carbon neutral ramp (matching `--text-secondary` / `--border`), and a
* categorical `range.category` palette transcribed from Carbon's data-viz
* 14-color pairing (white theme for light, g100 for dark — see
* carbon-charts `packages/core/scss/_color-palette.scss`). This is the
* expressive "free color" layer (doc §3.5, §5).
*/
import type { Config } from 'vega-lite';
import type { UiTheme } from './theme';
const PLEX = '"IBM Plex Sans", system-ui, -apple-system, sans-serif';
/** Carbon data-viz 14-color categorical palette — white (light) theme. */
const lightCategory = [
'#6929c4', // purple 70
'#1192e8', // cyan 50
'#005d5d', // teal 70
'#9f1853', // magenta 70
'#fa4d56', // red 50
'#520408', // red 90
'#198038', // green 60
'#002d9c', // blue 80
'#ee5396', // magenta 50
'#b28600', // yellow 50
'#009d9a', // teal 50
'#012749', // cyan 90
'#8a3800', // orange 70
'#a56eff', // purple 50
];
/** Carbon data-viz 14-color categorical palette — g100 (dark) theme. */
const darkCategory = [
'#8a3ffc', // purple 60
'#33b1ff', // cyan 40
'#007d79', // teal 60
'#ff7eb6', // magenta 40
'#fa4d56', // red 50
'#fff1f1', // red 10
'#6fdc8c', // green 30
'#4589ff', // blue 50
'#d02670', // magenta 60
'#d2a106', // yellow 40
'#08bdba', // teal 40
'#bae6ff', // cyan 20
'#ba4e00', // orange 60
'#d4bbff', // purple 30
];
export const lightChartConfig: Config = {
background: 'transparent',
font: PLEX,
title: { fontSize: 16, fontWeight: 600, color: '#161616' },
axis: {
domainColor: '#c6c6c6', // --border-strong (light)
gridColor: '#e0e0e0', // --border (light)
gridDash: [2, 2],
labelColor: '#525252', // --text-secondary (light)
titleColor: '#161616', // --text (light)
labelFontSize: 11,
titleFontSize: 12,
titleFontWeight: 600,
},
range: { category: lightCategory },
view: { stroke: 'transparent' },
};
export const darkChartConfig: Config = {
background: 'transparent',
font: PLEX,
title: { fontSize: 16, fontWeight: 600, color: '#f4f4f4' },
axis: {
domainColor: '#525252', // --border-strong (dark)
gridColor: '#393939', // --border (dark)
gridDash: [2, 2],
labelColor: '#a8a8a8', // --text-secondary (dark)
titleColor: '#f4f4f4', // --text (dark)
labelFontSize: 11,
titleFontSize: 12,
titleFontWeight: 600,
},
range: { category: darkCategory },
view: { stroke: 'transparent' },
};
const CHART_CONFIG: Record<UiTheme, Config> = {
light: lightChartConfig,
dark: darkChartConfig,
};
/** The Vega-Lite config for a UI theme — the only theme → config mapping. */
export function chartConfigFor(theme: UiTheme): Config {
return CHART_CONFIG[theme];
}