Color

Color in a chart carries data. Pick the palette from the kind of data, use as few colors as the message needs, and never let color be the only signal.

Match the palette to the data

Three kinds of data, three kinds of palette.

  • Categories with no order (products, regions, channels) take a categorical palette: distinct hues of similar weight, so no category looks more important than another.
  • Ordered values (low to high, a rate, a count) take a sequential palette: one hue from light to dark, where darker means more.
  • Values around a real midpoint (zero change, a target, an average) take a diverging palette: two hues that meet at a neutral middle. Without a midpoint the reader looks for one that is not there, so use sequential instead.

Never use a rainbow for numbers. Its hues have no order the reader can see, and its bright bands create edges that are not in the data.

Fewer colors

Every extra color is one more key the reader has to remember.

Keep a chart to about six series colors. Past that, group the smallest series into "Other", or split the chart into small multiples.

To make one point, color the series it is about and draw the rest in gray. The accent palette is built for this.

Same color, same meaning

Readers learn a color once and carry it from chart to chart.

Give each series one color across every chart on a page, and keep it when the series is filtered out of one chart. Map colors to series names, not to positions in the data, so a reordered or missing series does not shift the others.

Keep green and red for good and bad. Using them as two ordinary series colors makes the reader read a judgment into them.

Contrast

Marks need to stand out from the surface, and text needs more.

  • Lines, bars, points and the edges of areas need a contrast ratio of at least 3:1 against the chart surface.
  • Text needs at least 4.5:1. Never put text on a series color: a label that reads on one bar is lost on the next. Place labels beside the mark, or on the surface.

Check your palette against the surface it sits on, in light and dark mode. Light tints in the later steps of a palette are the first to fall short. Use emphasis="strong" for thin lines and small points.

Color vision

No palette works for every reader, so color is never the only signal.

Pair every color with a second cue: a direct label on the series, a dash pattern on a line, a marker shape on a point, or a texture on an area. Check the chart with a color vision simulator in your browser's developer tools. See Accessibility.

The kit's palettes

Every chart in the kit, whatever the library, colors its series from one of three palettes.

  • mono is six shades of one color, from full strength to a light tint. It is the default, built from colorPrimary. Use it for ordered values, or for one or two series.
  • accent is one color followed by five grays. Use it to highlight the first series and push the rest back.
  • multi is twelve colors: eleven distinct hues and a gray, with a separate list for light and dark mode. Use it for categories with no order.

Past a palette's length the colors start again, mixed further into the surface on each round. A seventh mono series reads as a lighter copy of the first, so plan for six series at most with mono and accent.

Because mono is the default, a chart of unordered categories reads as ranked unless you pass palette="multi".

Choosing one in code

The same three props on every chart component, and a scope for a whole region.

EChart, Recharts' ChartContainer and TanStack's Chart take palette, color and emphasis.

<ChartContainer config={config} palette="multi">
<BarChart data={signups}>...</BarChart>
</ChartContainer>
<EChart option={option} palette="accent" color="emerald" />
<Chart definition={definition} ariaLabel="Weekly orders" emphasis="strong" />

<ChartPalette> sets the palette for every chart inside it, and for anything else that reads chartColors.series, such as a legend you build beside the chart. It adds no box to the layout. Each chart module re-exports it.

import { ChartPalette, chartColors } from "@/components/ui/recharts-chart";
<ChartPalette palette="multi">
<Card title="Sign-ups by channel">
<ChartContainer config={config}>...</ChartContainer>
<Badge color={chartColors.series[0]} text="Organic" />
</Card>
</ChartPalette>;
Chart chrome
Everything that is not a series reads a role color, so the chrome follows the theme. Each role maps to a theme token unless the theme file sets its own value.

Dark mode

Chart colors follow the theme's mode with no extra code.

The palettes and the chrome are CSS variables, so a chart repaints when the mode changes and inside a ThemeScope. mono and accent mix their tints into the current surface, and multi switches to its dark list. Check contrast in both modes all the same: a hue that reads on white can sink into a dark surface.

Changing the palettes

The charts block of your theme file decides the palettes for every chart.

Each value names a token (colorPrimary, amber6, colorSplit) or a CSS color. A palette takes one list for both modes, or a light and a dark list. strong holds the emphasis="strong" lists, and a palette left out there is derived one step darker.

"charts": {
"default": "multi",
"multi": {
"light": ["volcano5", "teal5", "indigo4", "amber4", "sky4", "slate5"],
"dark": ["volcano7", "teal8", "indigo8", "amber8", "sky8", "slate8"]
},
"grid": "colorSplit",
"axis": "colorBorder",
"label": "colorTextTertiary",
"text": "colorTextSecondary",
"positive": "colorSuccess",
"negative": "colorError"
}

Run tokens:build after editing it. default picks the palette a chart uses when it passes none. The Theming page covers the rest of the file.

Palette props
Taken by EChart, ChartContainer, the TanStack Chart and ChartPalette.