Color by value
A series normally takes one color from the palette. Point colorProperty at a data property and each bar is colored per row instead, mapped through the series' colorScale ramp — a second measure encoded on the same bars.
ts
// colorProperty reads a color value per data row and maps it through the
// series colorScale ramp — here bar height is revenue while fill encodes
// margin, a second measure on the same bars. The row without a margin value
// falls back to colorScale.missing.
import type { MochartInputConfig } from '@mochart/core';
export const config: MochartInputConfig = {
version: '1.0.0',
title: { text: 'Revenue Shaded by Margin' },
legend: { visible: true },
categoryAxis: { property: 'product', type: 'string', scale: 'ordinal' },
valueAxes: [{ id: 'VA0', title: 'Revenue ($k)' }],
series: [
{
property: 'revenue',
title: 'Revenue (shaded by margin %)',
renderer: 'bar',
// Bar fills default to half opacity; full opacity keeps the ramp true.
shapeStyle: { normal: { strokeOpacity: 1, fillOpacity: 1 } },
colorProperty: 'margin',
colorScale: { interpolation: 'lab', min: '#cde2fb', max: '#0d366b' }
}
]
};
export const data = [
{ product: 'Laptops', revenue: 840, margin: 9 },
{ product: 'Phones', revenue: 720, margin: 14 },
{ product: 'Tablets', revenue: 310, margin: 12 },
{ product: 'Monitors', revenue: 260, margin: 21 },
{ product: 'Audio', revenue: 190, margin: 28 },
{ product: 'Accessories', revenue: 130, margin: 34 },
// No margin reported — colorScale.missing (default gray) colors this bar.
{ product: 'Services', revenue: 110 }
];How it works
- Each row's
colorPropertyvalue maps linearly fromcolorScale.mintocolorScale.maxacross the property's extent in that series — the palest bar is always the smallest color value and the darkest the largest, whatever the numbers are. PointcolorPropertyat the series' ownpropertyto shade each bar by its own value instead of a second measure. colorScale.interpolationpicks the d3 color space to interpolate in (rgb,hsl,lab,hcl). SettingcolorPropertyis the switch: once it's set, the scale defaults tohclthrough#8f8fff→#0000ff— the example above overrides the ramp, everything else is defaults.- Per-row color applies to
barseries (including floating bars viarangeProperty) — line and area shapes and markers keep their single series color. - Bar fills default to half opacity, which dilutes the ramp against the background; the example sets
shapeStyle.normal's two opacities to1so the colors read true. - A row without a color value gets
colorScale.missing(default#cccccc, the Services bar above); set it tonullto fall back to the series' own style colors instead. - The series' legend and tooltip color chips become a min→max ramp swatch, so the legend doubles as a compact color key (
showColorInLegend/showColorInTooltipturn the chips off). - The tooltip shows the series value, not the color value; point
tooltipPropertyat the color property to surface it, as the heatmap does.
Diverging around a base
Set colorScale.base.value and the ramp splits in two: one color pair above the threshold, another below — growth in blue, decline in red.
ts
// colorScale.base.value splits the ramp in two: values above the base
// interpolate through the above pair, values below through the below pair.
// Each min/max anchors to its half's data domain — belowMin sits at the most
// negative value, so the saturated color goes there for the classic
// palest-at-the-base diverging look.
import type { MochartInputConfig } from '@mochart/core';
export const config: MochartInputConfig = {
version: '1.0.0',
title: { text: 'Revenue Shaded by Growth' },
categoryAxis: { property: 'region', type: 'string', scale: 'ordinal' },
valueAxes: [{ id: 'VA0', title: 'Revenue ($k)' }],
series: [
{
property: 'revenue',
title: 'Revenue (shaded by YoY growth)',
renderer: 'bar',
shapeStyle: { normal: { strokeOpacity: 1, fillOpacity: 1 } },
colorProperty: 'growth',
colorScale: {
interpolation: 'hcl',
base: {
value: 0,
aboveMin: '#8f8fff',
aboveMax: '#0000ff',
belowMin: '#ff0000',
belowMax: '#ff8f8f'
}
}
}
]
};
export const data = [
{ region: 'North', revenue: 620, growth: 12 },
{ region: 'South', revenue: 540, growth: -4 },
{ region: 'East', revenue: 480, growth: 22 },
{ region: 'West', revenue: 450, growth: -11 },
{ region: 'Central', revenue: 300, growth: 3 },
{ region: 'Export', revenue: 210, growth: 35 }
];- With
base.valueset,min/maxare ignored and the fourbasecolors take over. Each anchors to its half's data domain:aboveMinsits at the base andaboveMaxat the highest value, whilebelowMinsits at the most negative value andbelowMaxat the base. For the classic diverging look — palest at the base, saturated at the extremes — order the below pair saturated → pale, as above (the defaults run the below pair the other way). - Each half fits its own side of the color property's extent, so the deepest red and deepest blue always mark the current extremes.
- The base splits only the colors. Here the bars measure revenue (all positive) while the color diverges on growth; when the color property is the series' own values, pin the value axis with
baseso the bars grow out of the same divide the colors split on.
For value-colored grids — rows of full-width bars sharing one global ramp — see the heatmap recipe.