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
| Order | Colour | Swatch | Token | Hex | Notes |
|---|---|---|---|---|---|
| 1st | Green | —op-color-green-30 | #566620 | Dark olive — the brand hue at the lowest step that passes 3:1. 6.12:1 vs background. | |
| 2nd | Blue | —op-color-blue-50 | #2684D5 | Vivid mid-blue, maximally distinct in hue from green. 3.8:1 vs background. | |
| 3rd | Orange | —op-color-orange-60 | #E05C34 | Warm, clearly different from blue and green in hue. 3.53:1 vs background. | |
| 4th | Teal | —op-color-teal-30 | #2C6C77 | Deep blue-green, distinct from both blue and green. 5.77:1 vs background. | |
| 5th | Purple | —op-color-purple-30 | #563D79 | Use only when 5 series are genuinely required. 8.68:1 vs background. | |
| 6th | Yellow | —op-color-yellow-40 | #9F7709 | Use 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.
| Series | Token | Contrast vs bg | Safe for |
|---|---|---|---|
| 1st — Green | —op-color-green-30 | 6.1:1 ✓ | Filled areas, lines, markers |
| 2nd — Blue | —op-color-blue-50 | 3.8:1 ✓ | Filled areas and lines |
| 3rd — Orange | —op-color-orange-60 | 3.5:1 ✓ | Filled areas and lines |
| 4th — Teal | —op-color-teal-30 | 5.8:1 ✓ | Filled areas, lines, markers |
| 5th — Purple | —op-color-purple-30 | 8.7:1 ✓ | Filled areas, lines, markers |
| 6th — Yellow | —op-color-yellow-40 | 4.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-100green-200green-300green-400green-500green-600green-700Start 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-100teal-200teal-300teal-400teal-500teal-600teal-700Start 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-100neutral-200neutral-300neutral-400neutral-500neutral-600neutral-700Start 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 positiveteal-600Positiveteal-400Mild positiveteal-200Slight positiveneutral-50Midpointred-200Slight negativered-300Mild negativered-400Negativered-600Strong negativeMidpoint 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 type | Recommended steps | Token range |
|---|---|---|
| 3-point (positive / neutral / negative) | 3 steps | teal-400, neutral-50, red-400 |
| 5-point Likert | 5 steps | teal-600, teal-200, neutral-50, red-200, red-400 |
| 7-point Likert or continuous diverging | 7–9 steps | teal-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 element | Contrast requirement | Against |
|---|---|---|
| Bar fill | 3:1 minimum | Chart background (neutral-50) |
| Adjacent bars (grouped bar chart) | 3:1 minimum | Adjacent bar colour |
| Line stroke | 3:1 minimum | Chart background and any overlapping areas |
| Data point marker | 3:1 minimum | Background and adjacent colours |
| Pie / donut segment boundary | 3:1 minimum between adjacent segments | Neighbouring segment colour |
| Axis lines and gridlines | 3:1 minimum | Chart 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-describedbysummary 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 role | Swatch | Token | Hex |
|---|---|---|---|
| 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?