Colors

Categorical, sequential, semantic, and diverging chart color palettes


The data visualization color palettes are based on Pluma's color palette, and are dark-mode aware. They are designed to be accessible, consistent, and predictable across a variety of chart types and data representations, ensuring that colors convey meaning effectively while maintaining visual harmony.

The palettes are categorized into four main types:

  • Categorical: For distinct, unrelated series.
  • Sequential: For ordered data, where color intensity represents magnitude.
  • Semantic: For series where color conveys specific meaning (e.g., success, warning).
  • Diverging: For data that diverges around a central point, or for highlighting differences between two extremes.

How to use the palettes

Each palette and color is available as a color token, with data-viz-prefixed names. For convenience, the @customerio/pluma-components/data-viz package exports utilities to retrieve the right palette subset for your needs. The utilities are:

  • defaultColor(): Returns the default color descriptor for single-series charts.
  • categorical(n): Returns the first n slots of the categorical palette (1 to 10).
  • sequential(variant, n): Returns a subset of the sequential palette for the specified variant (e.g., 'blue', 'teal') and count n (1 to 7).
  • semantic(role, n): Returns a subset of the semantic palette for the specified role (e.g., 'success', 'warning') and count n (1 to 7).
  • diverging(palette): Returns the full diverging palette for the specified palette (e.g., 'temperature', 'neutral', 'cool').

Each returns an array of DataVizColor descriptors (a single one for defaultColor), which is an object with the following keys:

  • type: The palette type ('categorical', 'sequential', 'semantic', or 'diverging').
  • name: A unique name for the color, derived from its token name.
    • This can be used as a stable and unique key for chart series, tying color names - and their respective color token - to each series.
  • token: The CSS variable reference for the color (e.g., var(--data-viz-categorical-1)).
  • tokenHover: The CSS variable reference for the hover state of the color (e.g., var(--data-viz-categorical-1-hover)).

The count argument in each function is optional - if omitted, the full palette is returned. More details on the functions are available below in their respective sections.

Which palette to use

  • One series (sparkline, metric card, single trend): defaultColor() (categorical slot 1).
  • 2 to 10 unrelated series: categorical in slot order. For longer series, we recommend applying additional encodings (line style, dash, dot, or pattern) to avoid color confusion. Looping the categorical palette is not recommended, as it can lead to misinterpretation of series identity, but is possible as a last resort.
  • Series whose color means something (volume, gain, loss, warning, non-event): a semantic role palette with the subset matching the series count.
  • Magnitude on one measure (heatmap, choropleth, density): one sequential palette -- the full set for smooth fills, or a subset for identifiable bins.
  • Divergence around a midpoint: a diverging palette.

Categorical

These are best used for unrelated series, or when you want to distinguish between different categories without implying order, magnitude, or relation. The palette should be used in the provided order, as the colors are designed to be visually distinct and harmonious when used together.

color-data-viz-categorical-{n}
1
2
3
4
5
6
7
8
9
10

categorical(n) returns the first n slots of the categorical palette, where n can be any integer from 1 to 10. If n is omitted, the full palette is returned.

import { categorical } from '@customerio/pluma-components/data-viz';

const categoryColors = categorical(4);
// [ 
//  { type: 'categorical', name: 'categorical-1', token: 'var(--...-categorical-1)', tokenHover: 'var(--...-categorical-1-hover)' },
//  ...
// ]

Sequential

Sequential palettes are best for ordered data, where color intensity represents magnitude, or for representing related series.

Each sequential palette contains 7 steps. For each palette, smaller subsets with fewer steps are available, which are designed be visually distinct and harmonious at every count. For example, when showing 3 series specifically, the 3-step subset of a palette should be used, rather than trimming from the full set.

Dark mode does not mirror the light palette position for position. Each hue's dark ramp is anchored where that hue still reads as itself against a dark surface: yellow, teal and green only look like themselves when they are light, while red, purple and blue only do when they are dark. Those anchors are set per hue rather than by one formula, so a step does not carry identical contrast in both modes - check a chart in both when its colors matter.

Bluecolor-data-viz-sequential-blue-{n}
7 series
1
2
3
4
5
6
7
6 series
1
2
3
4
5
7
5 series
1
2
3
5
7
4 series
1
3
5
7
3 series
1
3
5
2 series
1
3
1 series
3
Greencolor-data-viz-sequential-green-{n}
7 series
1
2
3
4
5
6
7
6 series
1
2
3
4
5
7
5 series
1
2
3
5
7
4 series
1
3
5
7
3 series
1
3
5
2 series
1
3
1 series
3
Greycolor-data-viz-sequential-grey-{n}
7 series
1
2
3
4
5
6
7
6 series
1
2
3
4
5
7
5 series
1
2
3
5
7
4 series
1
3
5
7
3 series
1
3
5
2 series
1
3
1 series
3
Orangecolor-data-viz-sequential-orange-{n}
7 series
1
2
3
4
5
6
7
6 series
1
2
3
4
5
7
5 series
1
2
3
5
7
4 series
1
3
5
7
3 series
1
3
5
2 series
1
3
1 series
3
Pinkcolor-data-viz-sequential-pink-{n}
7 series
1
2
3
4
5
6
7
6 series
1
2
3
4
5
7
5 series
1
2
3
5
7
4 series
1
3
5
7
3 series
1
3
5
2 series
1
3
1 series
3
Purplecolor-data-viz-sequential-purple-{n}
7 series
1
2
3
4
5
6
7
6 series
1
2
3
4
5
7
5 series
1
2
3
5
7
4 series
1
3
5
7
3 series
1
3
5
2 series
1
3
1 series
3
Redcolor-data-viz-sequential-red-{n}
7 series
1
2
3
4
5
6
7
6 series
1
2
3
4
5
7
5 series
1
2
3
5
7
4 series
1
3
5
7
3 series
1
3
5
2 series
1
3
1 series
3
Tealcolor-data-viz-sequential-teal-{n}
7 series
1
2
3
4
5
6
7
6 series
1
2
3
4
5
7
5 series
1
2
3
5
7
4 series
1
3
5
7
3 series
1
3
5
2 series
1
3
1 series
3
Yellowcolor-data-viz-sequential-yellow-{n}
7 series
1
2
3
4
5
6
7
6 series
1
2
3
4
5
7
5 series
1
2
3
5
7
4 series
1
3
5
7
3 series
1
3
5
2 series
1
3
1 series
3

Sequential palettes can be accessed using the sequential(variant, n) function, where variant is the name of the palette (e.g., 'blue', 'teal') and n is the number of steps desired (1 to 7). If n is omitted, the full palette is returned.

import { sequential } from '@customerio/pluma-components/data-viz';

const sequentialColors = sequential('blue', 3);
// [ 
//  { type: 'sequential', name: 'blue-2', token: 'var(--...-blue-2)', tokenHover: 'var(--...-blue-2-hover)' },
//  ...
// ]

Semantic

The semantic palettes are aliases of the sequential ones, but with a role that conveys meaning. Use these instead of sequential palettes when the series' are associated with a specific meaning. The roles are:

  • neutral (grey): for series that are neutral or non-critical, such as background or reference data.
  • information (blue): for series that convey information or status, such as notifications or updates.
  • success (green): for series that indicate success or positive outcomes.
  • caution (yellow): for series that indicate caution or warning.
  • critical (red): for series that indicate critical or error states.
neutral (grey)color-data-viz-semantic-neutral-{n}
7 series
1
2
3
4
5
6
7
6 series
1
2
3
4
5
7
5 series
1
2
3
5
7
4 series
1
3
5
7
3 series
1
3
5
2 series
1
3
1 series
3
information (blue)color-data-viz-semantic-information-{n}
7 series
1
2
3
4
5
6
7
6 series
1
2
3
4
5
7
5 series
1
2
3
5
7
4 series
1
3
5
7
3 series
1
3
5
2 series
1
3
1 series
3
success (green)color-data-viz-semantic-success-{n}
7 series
1
2
3
4
5
6
7
6 series
1
2
3
4
5
7
5 series
1
2
3
5
7
4 series
1
3
5
7
3 series
1
3
5
2 series
1
3
1 series
3
caution (yellow)color-data-viz-semantic-caution-{n}
7 series
1
2
3
4
5
6
7
6 series
1
2
3
4
5
7
5 series
1
2
3
5
7
4 series
1
3
5
7
3 series
1
3
5
2 series
1
3
1 series
3
critical (red)color-data-viz-semantic-critical-{n}
7 series
1
2
3
4
5
6
7
6 series
1
2
3
4
5
7
5 series
1
2
3
5
7
4 series
1
3
5
7
3 series
1
3
5
2 series
1
3
1 series
3

Semantic palettes can be accessed using the semantic(role, n) function, where role is the semantic role (e.g., 'success', 'warning') and n is the number of steps desired (1 to 7). If n is omitted, the full palette is returned.

import { semantic } from '@customerio/pluma-components/data-viz';

const successColors = semantic('success', 3);
// [
//  { type: 'semantic', name: 'success-2', token: 'var(--...-success-2)', tokenHover: 'var(--...-success-2-hover)' },
//  ...
// ]

Diverging

Diverging palettes also convey meaning, but are designed for data that diverges around a central point, or for highlighting differences between two extremes.

coolcolor-data-viz-diverging-cool-{n}
1
2
3
4
5
6
7
8
9
10
11
neutralcolor-data-viz-diverging-neutral-{n}
1
2
3
4
5
6
7
8
9
10
11
temperaturecolor-data-viz-diverging-temperature-{n}
1
2
3
4
5
6
7
8
9
10
11

Diverging palettes can be accessed using the diverging(palette) function, where palette is the name of the diverging palette (e.g., 'temperature', 'neutral', 'cool'). The full diverging palette is returned.

import { diverging } from '@customerio/pluma-components/data-viz';

const temperatureColors = diverging('temperature');
// [
//  { type: 'diverging', name: 'temperature-1', token: 'var(--...-temperature-1)', tokenHover: 'var(--...-temperature-1-hover)' },
//  ...
// ]

Sample charts

Line - 4 series (categorical)
JanFebMarAprMayJun
Grouped bar - 3 series (categorical)
Donut - 6 segments (categorical)
Sparklines - single series (defaultColor)
Sessions
Revenue

Hover and selection

Every item returned by the utility functions carries a tokenHover, so a series can swap to color.tokenHover on interaction. The hover always moves in the emphasis direction -- darker in light mode, lighter in dark mode -- for every step of every palette, so lines, bars, and binned encodings all hover the same way.

Guidelines

Do
  • Use color consistently across charts, especially for multiple charts on the same view.
  • Use additional visual treatment (line style, pattern, or shape) in addition to color to differentiate between series.
  • Apply a separator (empty space) between stacked series as additional visual distinction between data.
  • Use categorical palettes to separate items into categories, but use the same color with physical separation for items in one category.
Don't
  • Mix categorical and semantic palettes in one chart.
  • Overlay text on chart fills. Labels go beside marks, in text tokens.

Extra: matching a Badge to a chart color

While we recommend using typical legend-style labels for chart series (a colored box with text beside it), we may sometimes reach to Badge components for additional visual representation of items falling into a particular category. The Badge itself doesn't come with color tokens to match to chart series exactly, but they can be customized on a case-by-case basis using CSS and color mixing.

This recipe recommends:

  • for background-color, mix the chart color 20% with surface-base (lightens in light mode, darkens in dark mode)
  • for text color, mix the chart color 40% with text-base (darkens in light mode, lightens in dark mode)

This produces Badges that are visually distinct, but still clearly associated with the chart color. The recommended mix percentages are designed to maintain accessibility (APCA and WCAG) in both light and dark modes.

Loading editor