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.
The sixteen colours
- black 0 8 bright black
- red 1 9 bright red
- green 2 10 bright green
- yellow 3 11 bright yellow
- blue 4 12 bright blue
- magenta 5 13 bright magenta
- cyan 6 14 bright cyan
- 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.
~/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 $
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; }
~/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
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
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
Save as ~/.config/kitty/kaisla.conf and add include kaisla.conf to kitty.conf.
Save as ~/.config/alacritty/kaisla.toml and list that path under import in alacritty.toml.
Save as ~/.config/foot/kaisla.ini and add include=~/.config/foot/kaisla.ini at the top of foot.ini.
Save as ~/.config/wezterm/colors/Kaisla.toml, then set config.color_scheme = 'Kaisla' in wezterm.lua.
Save as ~/.config/ghostty/themes/kaisla, then set theme = kaisla in the Ghostty config.
Paste into the schemes list in settings.json, then choose Kaisla as the colour scheme of a profile.
Append to ~/.Xresources, then run xrdb -merge ~/.Xresources. Read by xterm, urxvt and st with the xresources patch.
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.
What each slot is for
| Colour | Slot | Name | Hex | Contrast | What programs use it for |
|---|---|---|---|---|---|
| 0 | black | A surface one step off the background: status bars, inactive tabs | |||
| 1 | red | Errors, removed lines in a diff, archives in ls | |||
| 2 | green | Added lines in a diff, passing tests, executables in ls | |||
| 3 | yellow | Warnings, numbers, search matches | |||
| 4 | blue | Directories in ls, the path in a prompt, function names | |||
| 5 | magenta | Keywords, compiler warnings, images in ls | |||
| 6 | cyan | Symlinks in ls, hunk headers in a diff, compiler notes | |||
| 7 | white | Ordinary text | |||
| 8 | bright black | Comments, line numbers, autosuggestions | |||
| 9 | bright red | The same jobs as 1 to 6, wherever a program asks for emphasis or bold | |||
| 10 | bright green | ||||
| 11 | bright yellow | ||||
| 12 | bright blue | ||||
| 13 | bright magenta | ||||
| 14 | bright cyan | ||||
| 15 | bright white | Headings and anything meant to stand out from text |