13

We had six charts built by four people and they all looked slightly different: different grays for gridlines, different fonts, three shades of the same 'brand blue'. I wanted the model to generate a single theme object and make every chart consume it.

The trick was to define the palette as semantic roles (grid, axis, series-1..n, positive, negative, muted) rather than raw hex, and to forbid any literal color inside chart components. It also had to derive a sequential and a diverging scale from the brand hue so heatmaps stay on-brand.

Sharing the token spec, would love critiques on the diverging-scale approach.

THE PROMPT
Produce a single chart theme module (TypeScript) that every visualization in an app imports. Structure:

1. A `tokens` object with SEMANTIC roles only: background, gridline, axisLabel, axisLine, seriesCategorical (an ordered array of 8, colorblind-safe, checked against deuteranopia), positive, negative, neutral, annotation.
2. Derived scales from a single {brandHue} input: a sequential scale (light->brand, 7 stops) and a diverging scale (negative hue <- neutral -> positive hue, 9 stops) generated in OKLCH so lightness steps are perceptually even, not naive HSL.
3. Type tokens: font stack, and a modular type scale for title/label/tick.

Hard rules: chart components must reference tokens by role; a literal hex or px font-size inside any chart is a lint error you should call out. Provide a `withTheme(spec)` helper that injects these into a Vega-Lite config. Include a contrast check asserting axisLabel-on-background >= 4.5:1 and print the computed ratio. Do not use pure black (#000) or pure white (#fff) for text or background.
OKLCH for the sequential ramp is the right call, HSL ramps go muddy in the mid-tones every time. The perceptual evenness really shows on choropleths.viz_vince 2 months ago
add a comment

0 Answers

Your Answer