charts.palette
A categorical palette of n colour-blind-safe colours for chart series: Okabe-Ito or Paul Tol's bright scheme.
1.0.0 · published 2026-10-03 by charlie · Anterra
Pinned by 12 tests, run in TypeScript, Python and Rust.
What it does
The colours for a chart's series: `palette("okabe-ito", 3, false)` is `["#e69f00", "#56b4e9", "#009e73"]`. Both schemes are published sets chosen to stay distinguishable to people with the common colour-vision deficiencies (protanopia, deuteranopia) as well as to everyone else, which the default rainbow palettes of many chart libraries are not.
- `okabe-ito`, 8 colours: orange, sky blue, bluish green, yellow, blue, vermillion, reddish purple, black (Okabe and Ito, "Color Universal Design"). The published list starts with black; here black is moved to the end, so the first series are coloured and black stays free for axes and text until the eighth series. Otherwise the order and values are as published. - `tol-bright`, 7 colours: blue, red, green, yellow, cyan, purple, grey, in Paul Tol's default order.
For example
palette(okabe-ito, 3, false)→ #e69f00, #56b4e9, #009e73 three Okabe-Ito colours start with orange, not blackpalette(okabe-ito, 8, false)→ #e69f00, #56b4e9, #009e73, #f0e442, #0072b2, #d55e00, #cc79a7, #000000 all eight Okabe-Ito colours, black lastpalette(okabe-ito, 1, false)→ #e69f00 one colour
The function
The same function in TypeScript, Python and Rust, pinned by the same tests. Pick your language; the choice follows you around the registry.
export function palette(scheme: string, count: number, cycle: boolean): readonly string[]
| scheme | string | okabe-ito (8 colours) or tol-bright (7 colours) |
| count | int | how many colours, 0 or more |
| cycle | bool | repeat the scheme when count is larger than it; otherwise that is an error |
| returns | string[] | lowercase "#rrggbb", in the scheme's order |
Your code names it in one line, in the file that uses it
import { palette } from "#fune/charts.palette@^1";
Imports name this capability’s declared dependencies, which fune builds next to it in your project; each one links to its page.
import { PALETTES } from "./charts_palette_data.ts"; ← this capability’s own data, compiled from data/palettes.json into the same file by fune build
/**
* The first `count` colours of a named categorical scheme, in its order.
*
* A scheme has a fixed number of colours chosen to stay distinguishable to
* people with colour-vision deficiencies; asking for more is an error unless
* `cycle` says repeating them is acceptable, because a ninth series silently
* reusing the first colour is a misleading chart.
*/
export function palette(scheme: string, count: number, cycle: boolean): readonly string[] {
const colours = PALETTES.filter((c) => c.scheme === scheme)
.slice()
.sort((a, b) => a.position - b.position)
.map((c) => c.hex);
if (colours.length === 0) throw new RangeError(`unknown palette "${scheme}"; use okabe-ito or tol-bright`);
if (!Number.isInteger(count) || count < 0) {
throw new RangeError(`count must be a whole number of at least 0, received ${count}`);
}
if (count > colours.length && !cycle) {
throw new RangeError(`palette "${scheme}" has only ${colours.length} colours; pass cycle to repeat them`);
}
const out: string[] = [];
for (let i = 0; i < count; i++) out.push(colours[i % colours.length]);
return out;
}Install
fune build
With that line in your source, in a TypeScript project (language typescript in fune.project), fune build resolves it and nothing else, pins them in fune.lock, downloads only the TypeScript package of each, and builds the code above into your project’s .fune/build, one readable file per capability with a header linking back here. Or pin a range in fune.project and build in one step:
fune add charts.palette
The manifest, vectors and README with only the TypeScript implementation. Install it without the registry with fune add ./charts.palette-1.0.0-typescript.fune, or fetch it from a terminal with fune pull charts.palette@1.0.0:typescript.
The whole function, every language, is one file too: charts.palette-1.0.0.fune, 10,984 bytes, sha256 ddf253584a9c2cad154b151e0ff42da0782096c9ab4744064ea04fc36c25b126. It installs into a project of any language.
Customise it in your app
The seams this capability offers. Put a marker directly above a function of your own and fune build wires it into the built code; the package on the registry is not changed, the built file’s header lists it under CUSTOMISED, and fune hooks lists every hook in the project. How hooks work.
before — your function gets the arguments and returns them, changed or not, or throws to refuse the call.
// fune: before charts.palette
after — your function gets the result and the arguments, and returns the final result.
// fune: after charts.palette
replace — it requires no other capability, so there is no dependency to replace.
step — your function runs at a numbered point inside the function’s body, receives the in-scope values it names as parameters, and may return replacements. List the points with fune show charts.palette --steps.
// fune: step charts.palette after <n|label>
Tests
A version published now needs at least 8 tests for every function, and one that expects the error for each function that throws; the registry refuses it otherwise. fune verify --all runs each case in TypeScript, Python and Rust, and a project runs them again with fune verify. This page lists the cases; it does not run them. The exact JSON is vectors.json.
| Case | Arguments | Expected | |
|---|---|---|---|
| three Okabe-Ito colours start with orange, not black | okabe-ito, 3, false | → | #e69f00, #56b4e9, #009e73 |
| all eight Okabe-Ito colours, black last | okabe-ito, 8, false | → | #e69f00, #56b4e9, #009e73, #f0e442, #0072b2, #d55e00, #cc79a7, #000000 |
| one colour | okabe-ito, 1, false | → | #e69f00 |
| zero colours is an empty list | okabe-ito, 0, false | → | |
| all seven of Tol's bright scheme in his order | tol-bright, 7, false | → | #4477aa, #ee6677, #228833, #ccbb44, #66ccee, #aa3377, #bbbbbb |
| two of Tol's bright scheme | tol-bright, 2, true | → | #4477aa, #ee6677 |
| cycling repeats from the start | okabe-ito, 10, true | → | #e69f00, #56b4e9, #009e73, #f0e442, #0072b2, #d55e00, #cc79a7, #000000, #e69f00, #56b4e9 |
| cycling Tol's scheme past its seventh colour | tol-bright, 9, true | → | #4477aa, #ee6677, #228833, #ccbb44, #66ccee, #aa3377, #bbbbbb, #4477aa, #ee6677 |
| exactly the scheme's size needs no cycle | tol-bright, 7, false | → | #4477aa, #ee6677, #228833, #ccbb44, #66ccee, #aa3377, #bbbbbb |
| more colours than the scheme has, without cycle, is an error | okabe-ito, 9, false | → | error: palette "okabe-ito" has only 8 colours |
Show the other 2 tests
| Case | Arguments | Expected | |
|---|---|---|---|
| an unknown scheme is an error | rainbow, 3, false | → | error: unknown palette "rainbow" |
| a negative count is an error | okabe-ito, -1, false | → | error: count must be a whole number of at least 0 |
More from the author
The colours are data (`data/palettes.json`, one row per colour with its position and name), not code. They describe fixed published schemes rather than rules that change over time, so the file has no `effective` columns and is never pruned.
Asking for more colours than a scheme has is an error unless `cycle` is true: a ninth series quietly reusing the first colour makes two series look like one. With `cycle`, colours repeat from the start. All hex is lowercase `#rrggbb`.
Sources: M. Okabe and K. Ito, "Color Universal Design (CUD): How to make figures and presentations that are friendly to colorblind people" (2002, rev. 2008), https://jfly.uni-koeln.de/html/color_blind/ (RGB 0-255 values: black 0,0,0; orange 230,159,0; sky blue 86,180,233; bluish green 0,158,115; yellow 240,228,66; blue 0,114,178; vermillion 213,94,0; reddish purple 204,121,167); B. Wong, "Points of view: Color blindness", Nature Methods 8, 441 (2011); P. Tol, "Colour Schemes" (SRON technical note SRON/EPS/TN/09-002, 2021), https://sronpersonalpages.nl/~pault/ (bright: #4477AA, #EE6677, #228833, #CCBB44, #66CCEE, #AA3377, #BBBBBB).
Files
| Path | Bytes |
|---|---|
| README.md | 1,961 |
| data/palettes.json | 1,217 |
| impl/python.py | 944 |
| impl/rust.rs | 1,291 |
| impl/typescript.ts | 1,184 |
| vectors.json | 1,841 |