Selkie.git | docs/api/ | Selkie--Plot--Palette.md
NAME
====
Selkie::Plot::Palette - Colorblind-friendly series palettes and color ramps for chart widgets
SYNOPSIS
========
```raku
use Selkie::Plot::Palette;
# Series palettes — discrete colors for multi-series charts
my @colors = Selkie::Plot::Palette.series('okabe-ito');
# (0xE69F00, 0x56B4E9, 0x009E73, 0xF0E442, 0x0072B2,
# 0xD55E00, 0xCC79A7, 0x999999)
# Color ramps — continuous gradients for heatmaps
my @stops = Selkie::Plot::Palette.ramp('viridis');
# (0.0 => 0x440154, 0.25 => 0x3B528B, 0.5 => 0x21908C, ...)
# Sample a ramp at any position in [0, 1]
my $color = Selkie::Plot::Palette.sample('viridis', 0.42);
# → 0x2E6A8E (interpolated between 0.25 and 0.5 stops)
```
DESCRIPTION
===========
Two abstractions for chart colors:
* **Series palettes** — discrete lists of distinct colors for multi-series charts (BarChart with N categories, LineChart with N series). Defaults to [Okabe-Ito](https://jfly.uni-koeln.de/color/), designed to be distinguishable for the most common forms of colorblindness.
* **Color ramps** — continuous gradients sampled by a normalised position in `[0, 1]`, for heatmaps and other value-encoded color use. Defaults to [viridis](https://bids.github.io/colormap/), the perceptually uniform colormap that's been the matplotlib default since 2.0.
Both are *separate from* [Selkie::Theme](Selkie--Theme.md). Theme slots cover named chart elements (axis, gridlines, legend background); palettes cover data colors. Different access patterns, different homes.
Series palettes
---------------
* `okabe-ito` (default, 8 colors) — Okabe & Ito's palette, optimised for deuteranopia / protanopia / tritanopia. The original palette starts with pure black, which is invisible on dark backgrounds; this implementation substitutes `0x999999` as the first color so the palette works on either light or dark themes.
* `tol-bright` (7 colors) — Paul Tol's "bright" qualitative palette ([personal.sron.nl/~pault](https://personal.sron.nl/~pault/)). Higher saturation, also colorblind-safe.
* `tableau-10` (10 colors) — Tableau's category10 palette. Vivid and well-tested in business dashboards. Less colorblind-friendly than Okabe-Ito but maximises distinct hues for many series.
If a chart needs more series than its palette provides, the colors cycle. For more than ~8 series consider redesigning the chart (faceting, stacked layout, on-hover series isolation) — at a glance, the human eye can't reliably distinguish more than ~7 chart series by color alone.
Color ramps
-----------
All ramps are 5-stop. Sampling between stops uses straight linear interpolation in RGB space — perceptually correct interpolation would need OkLab or Lab conversion, which is overkill for terminal cells where adjacent values blur visually anyway.
* `viridis` (default for heatmaps) — perceptually uniform, colorblind-safe, prints reasonably in greyscale. The matplotlib default since 2.0.
* `magma` — like viridis but warmer (purple → red → cream).
* `plasma` — high-saturation gradient (deep blue → pink → orange).
* `coolwarm` — diverging blue→white→red, useful for signed data where 0 is special (correlations, deltas).
* `grayscale` — five steps of gray. Mostly for accessibility fallback or print contexts.
EXAMPLES
========
Coloring a multi-series LineChart
---------------------------------
```raku
my @palette = Selkie::Plot::Palette.series('okabe-ito');
my @series = (
{ label => 'cpu', values => @cpu, color => @palette[0] },
{ label => 'memory', values => @mem, color => @palette[1] },
{ label => 'iowait', values => @iowait, color => @palette[2] },
);
my $chart = Selkie::Widget::LineChart.new(:@series, :show-legend);
```
Driving a Heatmap with a custom ramp stop
-----------------------------------------
```raku
my $heatmap = Selkie::Widget::Heatmap.new(
data => @grid,
ramp => 'coolwarm',
);
# Or, for one-off color lookups in custom widget code:
my $color = Selkie::Plot::Palette.sample('viridis', $normalised-value);
```
Cycling a palette beyond its length
-----------------------------------
```raku
my @palette = Selkie::Plot::Palette.series('tol-bright'); # 7 colors
my $color-for = sub ($i) { @palette[$i mod @palette.elems] };
# Series 0..6 get distinct colors; 7 wraps to series 0's color.
```
SEE ALSO
========
* [Selkie::Plot::Scaler](Selkie--Plot--Scaler.md) — value→cell mapping
* [Selkie::Plot::Ticks](Selkie--Plot--Ticks.md) — nice-number axis labels
* [Selkie::Theme](Selkie--Theme.md) — chart-element styling slots (axis, legend bg, etc.)
### method series
```raku
method series(
Str:D $name = "okabe-ito"
) returns List
```
Return the named series palette as a list of 24-bit RGB integers. Defaults to `okabe-ito`. Throws on unknown names.
### method ramp
```raku
method ramp(
Str:D $name = "viridis"
) returns List
```
Return the named color ramp as a list of `Real =` UInt> Pairs, each pair being a position in `[0, 1]` mapped to a 24-bit RGB. Defaults to `viridis`. Throws on unknown names.
### method sample
```raku
method sample(
Str:D $name,
Real $t
) returns UInt
```
Sample a ramp at `$t ∈ [0, 1]`, returning the interpolated 24-bit RGB color. Out-of-range `$t` is clamped. Interpolation is linear in RGB space (not OkLab) — adequate for terminal cells.