Design System
Foundations · Data visualization

Data visualization.

Charts are design. Color carries meaning — categorical, sequential, diverging — and each carries it differently. This page defines the three viz palettes, pairing rules, and a small set of chart primitives built on top of Akku's existing tokens.

Categorical

For unordered, unrelated series. Hue changes; lightness stays close. Start with 3 — the brand ramps — then extend to 6 by sampling softer steps.

Brand 3 — the default

3 series

Iris, coral, teal at step 500. This is the right choice 90% of the time. If you need more, pair down or aggregate first.

iris-500 · #8D58FE
coral-500 · #FF7F50
teal-500 · #0ABAB5

Brand 6 — extended

6 series

When you truly need 6, alternate anchor (step 500) and soft (step 300) for each hue. Dark-light-dark rhythm keeps neighbors distinguishable.

iris-500
coral-500
teal-500
iris-300
coral-300
teal-300

Sequential

For ordered values from low to high — heatmaps, choropleths, density. Single hue, lightness encodes magnitude.

Iris sequential — primary

7 steps

The default for brand-aligned magnitude views. Skip the 50 step for surface-mounted charts so the lowest bin is still legible on white.

100
200
300
500
600
700
900

Teal sequential — cool

7 steps

Alternate when the chart needs to stay distinct from UI accents that are already iris.

100
200
300
500
600
700
900

Diverging

For data with a meaningful midpoint — sentiment, deviation from average, profit vs loss. Two hues, neutral center.

Coral ↔ Teal

9 steps · 0 = neutral

Warm-to-cool reads as "below ↔ above" almost universally. The rule near 500 stays visible against white surfaces; reserve steps 100 and 900 for dense small multiples.

coral-700
500
300
100
neutral
100
300
500
teal-700

State & neutral

Charts often need to call out a single value or provide a quiet baseline. Use state tokens for semantic call-outs (not for categorical series), and slate for grid lines, axes, and baselines.

Semantic + grid

Reserved use
success · above target warning · at risk danger · breach info · reference slate-300 · axis slate-200 · gridline slate-500 · label

Chart primitives

Six reference charts built only with tokens. Copy the SVG, swap the data.

Line · 3 series

Weekly active users

48.2k ▲ 12.4%
60k 40k 20k 0 Mon Tue Wed Thu Fri Sat Sun
Product Marketing Support

Stacked area · 3 series

Revenue by channel

$184k ▲ 8.1%
Q1 Q2 Q3 Q4 Q1
Direct Partner Referral

Grouped bar · 2 series

Activation vs retention

71.3% ▼ 2.1%
Free Starter Pro Team Ent
Activation Retention

Sparkline · 1 series

API latency (p95)

142ms ▼ 18ms
target 150ms
p95 latency target

Heatmap · sequential iris

Sign-ups by day & hour

Mon Tue Wed Thu Fri Sat Sun
low high

Donut · 3 slices

Traffic mix

184k visits
Direct 48%
Organic 32%
Referral 20%

Rules

Short list. Do this, not that.

Do

Use the brand-3 for categorical

Iris, coral, teal at step 500 is always the first choice. Three series is almost always enough — if you need more, aggregate first.

Don't

Don't mix state and categorical

Success-green, warning-amber and danger-red carry meaning. Never use them as generic chart colors — keep them for call-outs like targets, thresholds, or breach indicators.

Do

Encode ordinal with one hue

Sequential data (a scale, not categories) should use a single hue. Let lightness do the work — 100 to 900 within iris or teal.

Don't

Don't reach for rainbow

Resist the urge to add a fourth, fifth, sixth brand hue. If a chart genuinely needs 7+ series, it probably wants a different chart.

Do

Label directly where possible

End-of-line labels beat legends. Color is support, not the only signal — the name of the series should be the primary identifier.

Don't

Don't rely on color alone

Pair color with shape, dash, or position for anyone with color-vision differences. Diverging palettes especially need a clearly marked midpoint.

Tokens

Wire these aliases in components; never read raw ramps directly from charts.

/* Categorical */
--viz-cat-1: var(--iris-500);
--viz-cat-2: var(--coral-500);
--viz-cat-3: var(--teal-500);
--viz-cat-4: var(--iris-300);
--viz-cat-5: var(--coral-300);
--viz-cat-6: var(--teal-300);

/* Sequential — iris */
--viz-seq-0: var(--iris-100);
--viz-seq-1: var(--iris-200);
--viz-seq-2: var(--iris-300);
--viz-seq-3: var(--iris-500);
--viz-seq-4: var(--iris-600);
--viz-seq-5: var(--iris-700);
--viz-seq-6: var(--iris-900);

/* Diverging — coral ↔ teal */
--viz-div-neg-2: var(--coral-700);
--viz-div-neg-1: var(--coral-500);
--viz-div-neg-0: var(--coral-200);
--viz-div-mid:   #F1F5F9;
--viz-div-pos-0: var(--teal-200);
--viz-div-pos-1: var(--teal-500);
--viz-div-pos-2: var(--teal-700);

/* Chart scaffolding */
--viz-grid:   var(--rule);       /* #E2E8F0 */
--viz-axis:   #CBD5E1;
--viz-label:  var(--ink-subtle); /* #64748B */
--viz-target: var(--teal-500);