Functional Weave
Code in Rust

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 black
  • palette(okabe-ito, 8, false) → #e69f00, #56b4e9, #009e73, #f0e442, #0072b2, #d55e00, #cc79a7, #000000 all eight Okabe-Ito colours, black last
  • palette(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.

pub fn palette(scheme: &str, count: i64, cycle: bool) -> Vec<String>
schemestringokabe-ito (8 colours) or tol-bright (7 colours)
countinthow many colours, 0 or more
cycleboolrepeat the scheme when count is larger than it; otherwise that is an error
returnsstring[]lowercase "#rrggbb", in the scheme's order

Your code names it in one line, in the file that uses it

fune!(charts.palette@^1);  // then call palette(…)
impl/rust.rs · 30 lines · open · raw

Imports name this capability’s declared dependencies, which fune builds next to it in your project; each one links to its page.

use super::funejson::Value;  ← the fune runtime: the JSON value the test vectors use; fune build keeps it only where a signature takes one
use super::charts_palette_data::PALETTES;  ← 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.
/// Asking for more colours than the scheme has is an error unless `cycle`
/// says repeating them is acceptable.
///
/// # Panics
/// Panics on an unknown scheme, a negative count, or too many colours
/// without `cycle`.
pub fn palette(scheme: &str, count: i64, cycle: bool) -> Vec<String> {
    let mut rows: Vec<_> = PALETTES.iter().filter(|c| c.scheme == scheme).collect();
    rows.sort_by_key(|c| c.position);
    let colours: Vec<String> = rows.iter().map(|c| c.hex.to_string()).collect();
    if colours.is_empty() {
        panic!("unknown palette \"{}\"; use okabe-ito or tol-bright", scheme);
    }
    if count < 0 {
        panic!("count must be a whole number of at least 0, received {}", count);
    }
    if count as usize > colours.len() && !cycle {
        panic!("palette \"{}\" has only {} colours; pass cycle to repeat them", scheme, colours.len());
    }
    (0..count as usize).map(|i| colours[i % colours.len()].clone()).collect()
}

pub fn fune_vector(args: &[Value]) -> Value {
    let out = palette(args[0].as_str(), args[1].as_i64(), args[2].as_bool());
    Value::Arr(out.iter().map(|s| Value::str(s)).collect())
}

Install

fune build

With that line in your source, in a Rust project (language rust in fune.project), fune build resolves it and nothing else, pins them in fune.lock, downloads only the Rust 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. A crate’s build.rs runs it before every compile. Or pin a range in fune.project and build in one step:

fune add charts.palette
Download for Rust charts.palette-1.0.0-rust.fune · 8,759 bytes sha256 ea033462cceffd9d13b2d9a80ece53c936a086ddaab2f2896d250fbabc5aeab8

The manifest, vectors and README with only the Rust implementation. Install it without the registry with fune add ./charts.palette-1.0.0-rust.fune, or fetch it from a terminal with fune pull charts.palette@1.0.0:rust.

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.

CaseArgumentsExpected
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
CaseArgumentsExpected
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

PathBytes
README.md1,961
data/palettes.json1,217
impl/python.py944
impl/rust.rs1,291
impl/typescript.ts1,184
vectors.json1,841