Skip to main content

Data Visualisation

Colour in charts

Chart colour in Open Point is drawn from the same token system as the rest of the product. There are three modes — categorical, sequential, and diverging — each with a defined palette, a defined order, and specific accessibility requirements that go beyond just picking from the ramp.

Categorical palettes

Use categorical colour when the data series represent distinct, unordered groups — different organisations, survey waves, response themes, or demographic segments. Each series gets one ramp colour. The order is fixed: it is not aesthetic preference, it is a deliberate sequence designed to maximise perceptual distinctiveness and minimise colour-blindness conflicts.

The six-step categorical sequence

OrderColourSwatchTokenHexNotes
1stGreen—op-color-green-30#566620Dark olive — the brand hue at the lowest step that passes 3:1. 6.12:1 vs background.
2ndBlue—op-color-blue-50#2684D5Vivid mid-blue, maximally distinct in hue from green. 3.8:1 vs background.
3rdOrange—op-color-orange-60#E05C34Warm, clearly different from blue and green in hue. 3.53:1 vs background.
4thTeal—op-color-teal-30#2C6C77Deep blue-green, distinct from both blue and green. 5.77:1 vs background.
5thPurple—op-color-purple-30#563D79Use only when 5 series are genuinely required. 8.68:1 vs background.
6thYellow—op-color-yellow-40#9F7709Use only when 6 series are required. Dark gold, distinct in hue from all others. 3.97:1 vs background.

Why this selection?

This is the mathematically optimal 6-colour combination from the Orbit token system — found by testing all possible step combinations across the six available hue families. Every entry passes WCAG 3:1 contrast against the chart background. Green leads using green-700 (dark olive, #566620) rather than the brand lime (green-400) because the lighter step fails 3:1. The sequence alternates warm and cool hues to maximise perceptual distinctiveness between adjacent entries. Red is excluded — it conflicts with green for red-green colour blind users and is reserved for error states.

Important limitation: no combination of 6 colours from the Orbit primitive token system achieves full grayscale distinctiveness between all pairs — the token system was designed for UI contrast (colour vs background), not data visualisation contrast (colour vs colour). This palette relies primarily on hue as the differentiator. Direct labels or distinct shapes are always required.

Contrast against light chart backgrounds

WCAG 1.4.11 (Non-text Contrast) requires 3:1 for graphical elements that convey information. All six entries in this palette pass that threshold.

SeriesTokenContrast vs bgSafe for
1st — Green—op-color-green-306.1:1 ✓Filled areas, lines, markers
2nd — Blue—op-color-blue-503.8:1 ✓Filled areas and lines
3rd — Orange—op-color-orange-603.5:1 ✓Filled areas and lines
4th — Teal—op-color-teal-305.8:1 ✓Filled areas, lines, markers
5th — Purple—op-color-purple-308.7:1 ✓Filled areas, lines, markers
6th — Yellow—op-color-yellow-404.0:1 ✓Filled areas and lines

All six entries pass 3:1 individually. However, the palette relies on hue as the primary differentiator between series — not luminance. Some pairs have similar brightness and will be hard to distinguish for users who cannot perceive colour differences. Direct labels on every series are always required. Distinct marker shapes are required for line and scatter charts.

What about more than 6 series?

If your chart genuinely requires more than 6 distinct categorical series, the chart is probably doing too much. Consider:

  • Grouping minor categories into an “Other” series
  • Breaking the chart into two or more smaller charts with a common axis
  • Switching to a table, which handles many categories more cleanly than a chart
  • Using a small-multiples layout instead of overlapping all series on one chart

If you’ve considered all alternatives and genuinely need more than 6, raise the case with the Design System Guild — don’t improvise colours outside the system.

How to apply it

Always pair colour with a label or pattern

Colour alone is never sufficient to distinguish categorical series. Every chart must pair colour with at least one other encoding:

  • Labels: Direct labels on the data (next to bars, at the end of lines) are the most reliable. They work even when the legend is detached or the chart is printed in greyscale.
  • Patterns: Fill patterns (hatching, dots, crosshatch) can supplement colour in bar and area charts. Use patterns sparingly — they can increase visual complexity.
  • Shape: In line and scatter charts, use distinct marker shapes (circle, square, triangle, diamond) in addition to colour. This is the most effective redundant encoding for line charts.
  • Legend with clear labels: The legend must always be present, clearly labelled, and associated with the chart via ARIA or a visible caption.

A reader who cannot distinguish the two series colours — whether due to colour blindness, a greyscale print, or a low-quality screen — must still be able to understand the chart.

Sequential palettes

Sequential palettes encode ordered, quantitative data — participation rates, response counts, density, magnitude. Use a single ramp stepping from a light step to a dark step. The reader should be able to read “lighter = less, darker = more” without needing to check the legend.

Sequential palettes are most commonly used in:

  • Choropleth maps where regions are shaded by a single metric
  • Heatmaps showing intensity across two dimensions
  • Single-metric bar charts where the bars are shaded by value rather than by category

Recommended sequential palettes

Green — positive quantity

Use for: participation rate, approval score, completion percentage, any metric where higher is positive

green-100
green-200
green-300
green-400
green-500
green-600
green-700

Start at step 100 (lowest value); end at step 700 (highest value). Use steps 200–600 for a gentler range.

Teal — neutral or informational quantity

Use for: response count, message volume, activity level — where the quantity is neutral, not inherently good or bad

teal-100
teal-200
teal-300
teal-400
teal-500
teal-600
teal-700

Start at step 100 (lowest value); end at step 700 (highest value).

Neutral — density or emphasis

Use for: maps where the data has no inherent positive/negative valence, or when colour saturation would distract from the data shape

neutral-100
neutral-200
neutral-300
neutral-400
neutral-500
neutral-600
neutral-700

Start at step 100 (lowest value); end at step 700 (highest value).

Sequential accessibility note

Sequential palettes are generally more accessible than categorical ones for colour-blind readers, because the encoding is lightness rather than hue. However, step ranges that are too compressed (e.g. steps 300–500 only) may not provide enough perceptual difference. Always test across the full intended data range before publishing. Use at least 5 distinct lightness steps for any sequential scale with more than 4 data buckets.

Diverging palettes

Diverging palettes are for data with a meaningful midpoint — a value where “neither direction” is the reference, and the two directions carry distinct meaning. Common uses in engagement work:

  • Sentiment: support/oppose, agree/disagree, positive/negative
  • Change from baseline: above/below target, gain/loss
  • Likert scale responses: from “strongly disagree” to “strongly agree”

The Open Point diverging palette runs from teal (positive or agreeing extreme) through a neutral midpoint to red (negative or opposing extreme).

Teal → Neutral → Red diverging palette

teal-700Strong positive
teal-600Positive
teal-400Mild positive
teal-200Slight positive
neutral-50Midpoint
red-200Slight negative
red-300Mild negative
red-400Negative
red-600Strong negative

Midpoint token: —op-color-neutral-95 (#FFF6E9). Positive extreme: —op-color-teal-30. Negative extreme: —op-color-red-40.

Why teal rather than green for diverging?

Green is the brand primary colour, which means readers associate it with “Open Point” and with “correct” or “positive” UI states. Using green as the diverging positive extreme is tempting — but it creates two problems.

First, in a diverging chart showing support/oppose sentiment, pairing green (positive) against red (negative) produces a palette that is almost completely unreadable for people with red-green colour vision deficiency. Teal and red are significantly more distinguishable for deuteranopic and protanopic readers because teal contains a strong blue component that red does not.

Second, the brand green (#BACE42) is a mid-range value that does not darken well into a “very positive” extreme while remaining perceptually consistent with the teal–red axis. Teal has a deeper dark step (teal-700) that works better as a “strongly agree” endpoint.

Use green in diverging charts only if the data is explicitly about Open Point brand health, where the brand association is intentional and appropriate.

Number of steps to use

Scale typeRecommended stepsToken range
3-point (positive / neutral / negative)3 stepsteal-400, neutral-50, red-400
5-point Likert5 stepsteal-600, teal-200, neutral-50, red-200, red-400
7-point Likert or continuous diverging7–9 stepsteal-700 to teal-200, neutral-50, red-200 to red-600

Colour blindness and the categorical palette

Red-green colour vision deficiency — collectively, deuteranopia and protanopia — affects approximately 8% of men and 0.5% of women. In a population of 1,000 people, that is roughly 40–50 people who cannot reliably distinguish red from green. For a government consultation platform that may reach tens of thousands of residents, this is not a minor edge case.

The categorical palette is specifically ordered to prevent red and green from being adjacent. Green is position 1; red is position 6. In practice, this means:

  • In a 2-series chart, you have green and teal — no red in view at all.
  • In a 3-series chart, you have green, teal, and blue — still no red.
  • Red only appears when there are 6 series, at which point it is surrounded by orange (position 5) and is not adjacent to green (position 1 in the legend, visually separated in the chart).

This is why the sequence is not optional. If you reorder the palette to put red and green next to each other — even if it “looks better” visually — you will produce a chart that is inaccessible for approximately 1 in 12 male readers.

Testing your charts for colour blindness

Before publishing any chart with 3 or more colour series, simulate it in:

  • Deuteranopia simulation (loss of green cone sensitivity) — the most common form
  • Protanopia simulation (loss of red cone sensitivity)
  • Greyscale — confirms the chart works in print and for all colour vision types

macOS Accessibility Inspector and browser devtools (Chrome’s “Emulate vision deficiencies” in the Rendering panel) can do this without additional tooling. Figma’s “Colour Blind” plugin is useful during design review.

Accessibility requirements

WCAG 2.1 Success Criterion 1.4.11 (Non-text Contrast, Level AA) requires that graphical objects — including chart bars, line segments, data points, and filled regions — achieve a contrast ratio of at least 3:1 against adjacent colours or backgrounds.

This means:

Chart elementContrast requirementAgainst
Bar fill3:1 minimumChart background (neutral-50)
Adjacent bars (grouped bar chart)3:1 minimumAdjacent bar colour
Line stroke3:1 minimumChart background and any overlapping areas
Data point marker3:1 minimumBackground and adjacent colours
Pie / donut segment boundary3:1 minimum between adjacent segmentsNeighbouring segment colour
Axis lines and gridlines3:1 minimumChart background
Data labels (text)4.5:1 minimum (WCAG 1.4.3 text contrast)The background behind the label

Note that data labels are text, so they are subject to the stricter 4.5:1 text contrast requirement (WCAG 1.4.3), not the 3:1 non-text requirement.

Watch out

Brand green (#BACE42) passes 3:1 for chart elements — but not for text

—op-color-green-60 (#BACE42) has a contrast ratio of approximately 1.76:1 against white (#FFFFFF). It fails WCAG AA for text (requires 4.5:1), and it fails WCAG AA for UI components and non-text elements (requires 3:1) on a pure white background.

However, the Open Point chart background is neutral-50 (#FFF6E9), not pure white. Against neutral-50, green-400 achieves approximately 1.82:1 — still below the 3:1 non-text threshold. This means green-400 as a chart bar fill on a neutral-50 background technically fails WCAG 1.4.11.

In practice, bars are not adjacent to the background at their full face area — they sit on a chart with an axis, and the perceptual boundary is at the bar edge against the gridline, not the bar face against the background. The token passes a practical reading test. However, you should never use green-400 as a text label colour on neutral-50, white, or any light surface — it will always fail 4.5:1. Use —op-color-green-30 or —op-color-text-primary for data labels on charts with green bars.

How to apply it

Pair colour with labels, patterns, and shape

The rule is simple: a reader who cannot perceive colour differences must be able to read your chart. Implement redundant encoding as follows:

  • Bar charts: Use direct data labels (the value above or inside the bar) and label the category axis clearly. For grouped or stacked bars, use fill patterns (available in the chart component library) in addition to colour.

  • Line charts: Use distinct marker shapes at data points (circle for series 1, square for series 2, triangle for series 3). Label lines directly at the endpoint rather than relying solely on a legend.

  • Pie and donut charts: Label segments directly with the category name and value. Never use a colour-only legend as the sole identification method.

  • Maps: Use a clear legend with labelled ranges, not just a colour gradient bar. Include a data table for the mapped metric.

  • All charts: Provide a programmatically accessible data table or a aria-describedby summary description so screen reader users receive the underlying data.

Token quick reference

The table below summarises the recommended token for each chart colour role. These are the values to reach for first — before considering any other step in the ramp.

Chart roleSwatchTokenHex
Primary categorical series—op-color-green-60#BACE42
Secondary categorical series—op-color-teal-60#5EC5DD
3rd categorical series—op-color-blue-60#24A4FC
4th categorical series—op-color-purple-60#A48BEA
5th categorical series—op-color-orange-60#E05C34
6th categorical series—op-color-red-60#C73D3D
Sequential low (positive scale)—op-color-green-90#EEF4C2
Sequential high (positive scale)—op-color-green-30#4D5714
Sequential low (neutral scale)—op-color-neutral-90#F0EAE1
Sequential high (neutral scale)—op-color-neutral-30#3D332B
Diverging positive extreme—op-color-teal-30#084F4B
Diverging midpoint—op-color-neutral-95#FFF6E9
Diverging negative extreme—op-color-red-40#AD1D13
Chart background—op-color-neutral-95#FFF6E9
Gridlines and axis lines—op-color-border-default#D6CABD
Axis labels and data labels (text)—op-color-text-primary#1E1E1E
Chart title—op-color-text-primary#1E1E1E
Chart caption / footnote—op-color-text-secondary#6B5F56

Was this page helpful?