/** * Theme Builder gallery specs (docs/chart-theming-scope.md §4.4). * * A fixed set of small, self-contained Vega-Lite specs the Theme Builder * renders side by side with the draft config, so an edit is previewed across * every chart surface a config styles: titles and subtitles, axes and grids, * facet headers, the major mark types, and — so every color control has a * mirror — each color family: categorical (`range.category`), sequential * (`range.heatmap` for rect, `range.ramp` for a continuous legend), and * diverging (`range.diverging`, selected by a quantitative color scale with a * `domainMid`). Inline data only, compact fixed sizes — swatches, not analyses. */ import type { JsonObject } from './spec-config'; const SCHEMA = 'https://vega.github.io/schema/vega-lite/v6.json'; export interface ThemePreviewSpec { /** Stable key for React lists and test assertions. */ id: string; /** What surface this card exercises (shown as the card caption). */ caption: string; spec: JsonObject; } const bar: ThemePreviewSpec = { id: 'bar', caption: 'Bar — title, axes, grid', spec: { $schema: SCHEMA, title: 'Revenue by region', width: 200, height: 140, data: { values: [ { region: 'North', revenue: 42 }, { region: 'South', revenue: 61 }, { region: 'East', revenue: 28 }, { region: 'West', revenue: 55 }, { region: 'Central', revenue: 47 }, ], }, mark: 'bar', encoding: { x: { field: 'region', type: 'nominal', axis: { labelAngle: 0 } }, y: { field: 'revenue', type: 'quantitative' }, }, }, }; const line: ThemePreviewSpec = { id: 'line', caption: 'Line — subtitle, series legend', spec: { $schema: SCHEMA, title: { text: 'Signups over time', subtitle: 'Weekly, by plan' }, width: 200, height: 140, data: { values: [ { week: 1, plan: 'Free', n: 20 }, { week: 2, plan: 'Free', n: 28 }, { week: 3, plan: 'Free', n: 26 }, { week: 4, plan: 'Free', n: 34 }, { week: 1, plan: 'Pro', n: 8 }, { week: 2, plan: 'Pro', n: 11 }, { week: 3, plan: 'Pro', n: 17 }, { week: 4, plan: 'Pro', n: 21 }, { week: 1, plan: 'Team', n: 3 }, { week: 2, plan: 'Team', n: 4 }, { week: 3, plan: 'Team', n: 9 }, { week: 4, plan: 'Team', n: 12 }, ], }, mark: { type: 'line', point: true }, encoding: { x: { field: 'week', type: 'quantitative', axis: { tickCount: 4 } }, y: { field: 'n', type: 'quantitative' }, color: { field: 'plan', type: 'nominal' }, }, }, }; const area: ThemePreviewSpec = { id: 'area', caption: 'Stacked area — categorical palette', spec: { $schema: SCHEMA, width: 200, height: 140, data: { values: [ { q: 1, channel: 'Web', v: 30 }, { q: 2, channel: 'Web', v: 36 }, { q: 3, channel: 'Web', v: 41 }, { q: 4, channel: 'Web', v: 38 }, { q: 1, channel: 'Store', v: 22 }, { q: 2, channel: 'Store', v: 19 }, { q: 3, channel: 'Store', v: 24 }, { q: 4, channel: 'Store', v: 27 }, { q: 1, channel: 'Partner', v: 12 }, { q: 2, channel: 'Partner', v: 16 }, { q: 3, channel: 'Partner', v: 14 }, { q: 4, channel: 'Partner', v: 18 }, ], }, mark: 'area', encoding: { x: { field: 'q', type: 'quantitative', axis: { tickCount: 4 } }, y: { field: 'v', type: 'quantitative' }, color: { field: 'channel', type: 'nominal' }, }, }, }; const scatter: ThemePreviewSpec = { id: 'scatter', caption: 'Scatter — gradient legend', spec: { $schema: SCHEMA, width: 200, height: 140, data: { values: [ { x: 4, y: 7, z: 12 }, { x: 8, y: 3, z: 31 }, { x: 12, y: 11, z: 45 }, { x: 16, y: 6, z: 22 }, { x: 20, y: 14, z: 60 }, { x: 24, y: 9, z: 38 }, { x: 28, y: 17, z: 74 }, { x: 32, y: 12, z: 51 }, { x: 36, y: 20, z: 88 }, ], }, mark: { type: 'point', filled: true, size: 80 }, encoding: { x: { field: 'x', type: 'quantitative' }, y: { field: 'y', type: 'quantitative' }, color: { field: 'z', type: 'quantitative' }, }, }, }; const heatmap: ThemePreviewSpec = { id: 'heatmap', caption: 'Heatmap — sequential scale', spec: { $schema: SCHEMA, width: 200, height: 140, data: { values: ['Mon', 'Tue', 'Wed', 'Thu'].flatMap((day, d) => ['AM', 'Noon', 'PM'].map((slot, s) => ({ day, slot, v: (d + 1) * (s + 2) * 3 })), ), }, mark: 'rect', encoding: { x: { field: 'day', type: 'nominal', axis: { labelAngle: 0 } }, y: { field: 'slot', type: 'nominal' }, color: { field: 'v', type: 'quantitative' }, }, }, }; const diverging: ThemePreviewSpec = { id: 'diverging', caption: 'Diverging — scale around a midpoint', spec: { $schema: SCHEMA, title: 'Net change by topic', width: 200, height: 140, data: { values: [ { topic: 'Cost', delta: -8 }, { topic: 'Speed', delta: -3 }, { topic: 'Help', delta: 1 }, { topic: 'Look', delta: 6 }, { topic: 'Value', delta: 11 }, ], }, mark: 'bar', encoding: { x: { field: 'topic', type: 'nominal', axis: { labelAngle: 0 } }, y: { field: 'delta', type: 'quantitative' }, // `domainMid` makes this a diverging color scale, so it reads from // `range.diverging` rather than `range.ramp`/`heatmap` (the others above). color: { field: 'delta', type: 'quantitative', scale: { domainMid: 0 }, legend: { title: null }, }, }, }, }; const donut: ThemePreviewSpec = { id: 'donut', caption: 'Donut — palette, symbol legend', spec: { $schema: SCHEMA, width: 200, height: 140, data: { values: [ { browser: 'Firefox', share: 32 }, { browser: 'Chrome', share: 41 }, { browser: 'Safari', share: 18 }, { browser: 'Other', share: 9 }, ], }, mark: { type: 'arc', innerRadius: 32 }, encoding: { theta: { field: 'share', type: 'quantitative' }, color: { field: 'browser', type: 'nominal' }, }, }, }; const facet: ThemePreviewSpec = { id: 'facet', caption: 'Facets — header labels', spec: { $schema: SCHEMA, width: 70, height: 110, data: { values: [ { team: 'Alpha', month: 'Jan', v: 14 }, { team: 'Alpha', month: 'Feb', v: 21 }, { team: 'Alpha', month: 'Mar', v: 17 }, { team: 'Beta', month: 'Jan', v: 9 }, { team: 'Beta', month: 'Feb', v: 13 }, { team: 'Beta', month: 'Mar', v: 19 }, { team: 'Gamma', month: 'Jan', v: 11 }, { team: 'Gamma', month: 'Feb', v: 8 }, { team: 'Gamma', month: 'Mar', v: 15 }, ], }, mark: 'bar', encoding: { x: { field: 'month', type: 'nominal', axis: { labelAngle: 0 } }, y: { field: 'v', type: 'quantitative' }, column: { field: 'team', type: 'nominal' }, }, }, }; /** The gallery, in display order — the three continuous-color examples * (scatter → ramp, heatmap → heatmap, diverging → diverging) sit together. */ export const THEME_PREVIEW_SPECS: ReadonlyArray = [ bar, line, area, scatter, heatmap, diverging, donut, facet, ];