Kaisla

Sixteen terminal colours from one.

Give Kaisla a base colour and it builds the whole ANSI palette around it. Check the result on real output, then copy the config for your terminal.

Start from
55
7.0:1
40
Background

The sixteen colours

  1. black 0 8 bright black
  2. red 1 9 bright red
  3. green 2 10 bright green
  4. yellow 3 11 bright yellow
  5. blue 4 12 bright blue
  6. magenta 5 13 bright magenta
  7. cyan 6 14 bright cyan
  8. white 7 15 bright white

Colours 0 to 7 stand above the line. Their bright twins, 8 to 15, hang below it.

On real output

A swatch tells you little. These are the same sixteen colours doing their usual jobs, on the background they will live on.

A shell session
~/src/kaisla main $ ls
app.js  index.html    publish.sh
docs    latest        shore.png
fonts   notes.tar.gz  style.css
~/src/kaisla main $ git status -sb
## main...origin/main [ahead 2]
 M app.js
 M style.css
?? fonts/
~/src/kaisla main $ node test.js
ok 1 - reads three and six digit hex
ok 2 - a grey base gives a grey palette
not ok 3 - blue stays in gamut at full saturation
# pass 2
# fail 1
~/src/kaisla main 1 $  
git diff
diff --git a/app.js b/app.js
index 3f2a91c..b07d5e4 100644
--- a/app.js
+++ b/app.js
@@ -58,6 +58,8 @@ function build(st) {
   for (i = 1; i < 7; i++) {
-    hue = base.h + OFFSETS[i];
-    out[i] = hslToHex(hue, sat, lum);
+    away = shortest(base.h - HOME[i]);
+    hue = HOME[i] + away * 0.35 * tint;
+    l = solve(want, chroma, hue, bgY);
+    out[i] = toHex(fit(l, chroma, hue));
   }
   return out;
 }
A compiler with complaints
~/src/reedbed main $ make
cc -Wall -O2 -c -o build/render.o src/render.c
src/render.c: In function ‘draw_row’:
src/render.c:88:9: warning: unused variable ‘pad’ [-Wunused-variable]
   88 |     int pad = cols - used;
      |         ^~~
src/render.c:104:5: error: ‘ncols’ undeclared (first use in this function)
  104 |     ncols = cols;
      |     ^~~~~
      |     cols
src/render.c:104:5: note: each undeclared identifier is reported only once
make: *** [Makefile:22: build/render.o] Error 1
An editor in 16-colour mode
 11 def contrast(a, b):
 12     """WCAG ratio between two colours."""
 13     la, lb = luminance(a), luminance(b)
 14     hi, lo = max(la, lb), min(la, lb)
 15     return (hi + 0.05) / (lo + 0.05)
 16 
 17 # under 4.5 is hard to read as body text
 18 for name, colour in palette.items():
 19     if contrast(colour, background) < 4.5:
 20         print(f"{name} is too close")
~
~
 NORMAL  contrast.py                              20,9   Bot 
Every colour as text, on every background
     bg   0    1    2    3    4    5    6    7  
 0  gYw  gYw  gYw  gYw  gYw  gYw  gYw  gYw  gYw 
 1  gYw  gYw  gYw  gYw  gYw  gYw  gYw  gYw  gYw 
 2  gYw  gYw  gYw  gYw  gYw  gYw  gYw  gYw  gYw 
 3  gYw  gYw  gYw  gYw  gYw  gYw  gYw  gYw  gYw 
 4  gYw  gYw  gYw  gYw  gYw  gYw  gYw  gYw  gYw 
 5  gYw  gYw  gYw  gYw  gYw  gYw  gYw  gYw  gYw 
 6  gYw  gYw  gYw  gYw  gYw  gYw  gYw  gYw  gYw 
 7  gYw  gYw  gYw  gYw  gYw  gYw  gYw  gYw  gYw 
 8  gYw  gYw  gYw  gYw  gYw  gYw  gYw  gYw  gYw 
 9  gYw  gYw  gYw  gYw  gYw  gYw  gYw  gYw  gYw 
10  gYw  gYw  gYw  gYw  gYw  gYw  gYw  gYw  gYw 
11  gYw  gYw  gYw  gYw  gYw  gYw  gYw  gYw  gYw 
12  gYw  gYw  gYw  gYw  gYw  gYw  gYw  gYw  gYw 
13  gYw  gYw  gYw  gYw  gYw  gYw  gYw  gYw  gYw 
14  gYw  gYw  gYw  gYw  gYw  gYw  gYw  gYw  gYw 
15  gYw  gYw  gYw  gYw  gYw  gYw  gYw  gYw  gYw 

Rows are text colours 0 to 15. The first column is the terminal background, the rest are colours 0 to 7 used as a background.

The six colours share one lightness, so none of them reads on top of another: that block in the middle is a blur, and it is meant to be. On a coloured background the text to use is row 0, which is what the status line in the editor sample does.

Copy the config

Terminal

Save as ~/.config/kitty/kaisla.conf and add include kaisla.conf to kitty.conf.

The link carries the base colour, the three sliders and the background. Nothing about your palette is sent to a server.

How the palette is built

Kaisla takes two things from your base colour: its hue and how vivid it is. How light or dark it is gets ignored, because lightness here is decided by the job each colour has to do.

Six colours that keep their names

Red, green, yellow, blue, magenta and cyan each have a home on the colour wheel. The base pulls all six towards itself, up to about a third of the way with Tint at 100. Start from a green and red drifts towards orange while blue drifts towards teal, but an error still looks like an error. The earlier version simply rotated the base hue, and gave you green errors if you started from blue.

Every colour as readable as the next

Each of the six is set to the same contrast ratio against the background: the number beside the Contrast slider. That is why blue comes out lighter than you might expect on a dark background. At equal lightness it is the one you squint at.

The sums are done in OKLCH. When a colour cannot be that vivid at that lightness inside sRGB, its saturation is lowered until it fits. The hue is left alone.

Bright means more contrast

The bright twins sit a third further from the background: lighter on a dark background, darker on a light one. Programs use them for emphasis, and emphasis that fades into the page is not much use.

Four greys in order

Black and white are not black and white here. Colours 0, 8, 7 and 15 are four steps away from the background, nearest first: a surface, a hint, ordinary text, emphasis. The order holds on a light background too, so comments printed in bright black stay quiet and text printed in white stays readable. All four carry a little of the base hue.

Where the base colour ends up

Brought to a readable lightness and otherwise untouched, it becomes the cursor. A muted version becomes the selection: select some text on this page to see it.

Hollow marks are the home hues. The dots are where the colours sit now, and the line points at your base.

What each slot is for

ColourSlotNameHexContrastWhat programs use it for
0blackA surface one step off the background: status bars, inactive tabs
1redErrors, removed lines in a diff, archives in ls
2greenAdded lines in a diff, passing tests, executables in ls
3yellowWarnings, numbers, search matches
4blueDirectories in ls, the path in a prompt, function names
5magentaKeywords, compiler warnings, images in ls
6cyanSymlinks in ls, hunk headers in a diff, compiler notes
7whiteOrdinary text
8bright blackComments, line numbers, autosuggestions
9bright redThe same jobs as 1 to 6, wherever a program asks for emphasis or bold
10bright green
11bright yellow
12bright blue
13bright magenta
14bright cyan
15bright whiteHeadings and anything meant to stand out from text