Skip to content
All projects

Case study / open source

Gogh

Color schemes for 19 terminal emulators, applied with one command. Started in 2013, now 1,250 themes from 177 contributors, with 10k+ stars.

The problem

Every terminal emulator keeps its colors somewhere different. GNOME Terminal reads them from dconf profiles, Alacritty and Kitty from config files, the Linux console from escape sequences. Changing a theme meant finding where your terminal hid its sixteen colors and typing them in by hand, once per machine and once per terminal.

Gogh started on November 18, 2013, inspired by the clean, minimal look of elementary OS: a way to try a color scheme in one step and keep it if you liked it.

One palette, many terminals

The whole project rests on one idea: a theme is data, and each terminal is an adapter. A theme is a small YAML file with sixteen ANSI colors, plus the background, foreground and cursor. apply-colors.sh works out which terminal it is running in and calls that terminal's own apply_* function, which writes the same palette in whatever shape that terminal expects.

Adding a terminal never touches the themes; adding a theme never touches the terminals. That split is why both lists could grow for more than a decade without the code growing with them.

Draculadark · Dracula
# 1,250 palettes, 19 terminals, one command
❯ ~/gogh bash -c "$(curl -fsSL https://gogh.website/gogh)" -- "Dracula"
❯ ~/gogh ls
data/  installs/  themes/  tools/  apply-colors.sh  gogh.sh  README.md
❯ ~/gogh echo $TERMINAL && gogg
gnome-terminal
bash: gogg: command not found
❯ ~/gogh 
The same session in 9 of Gogh's palettes.

One file to contribute

Gogh's themes come from its community: 177 people have contributed so far. So the path to contributing is a single file: drop a YAML file in themes/, open a pull request, done. Contributors never touch an installer, a JSON file or a list of names.

Pull requests are checked automatically: hex colors are uppercase, the file name matches the theme's name, the variant is dark or light, the name does not collide with an existing theme, and a theme pull request only touches themes/. Foreground-to-background contrast below 2.5:1 is reported, but it does not fail the check: contrast is a judgment call, not a fact like a mismatched file name. And only newly added themes are checked, so an old issue on a theme nobody is touching never blocks someone else's pull request.

Everything else is generated

Every push to master runs a ten-step pipeline (Python for the first nine, Deno for the last) that builds everything from those YAML files: the theme data as JSON, CSV and YAML, one install script per theme, the theme list inside gogh.sh, and a WCAG contrast report for every palette. Nothing generated is ever edited by hand, so nothing generated can drift from the themes.

Tested where it runs

A color scheme that only works on my machine is not much use. Sixteen of the nineteen supported terminals have their own Docker test in the repository, so a change to one adapter can be checked in that terminal without installing it.

Data anyone can use

The themes are also published as plain static files (JSON, CSV and YAML) that need no API key and have no rate limit. Each palette carries a hash of its colors, so palettes can be compared and duplicates spotted across the whole collection. Other tools build on that data, including my own Omarchy × Gogh, which turns any Gogh palette into a full desktop theme.

What it taught me

  • Make contributing a single file. Every step you remove from a contributor's path is one fewer reason to give up halfway.
  • Generate, don't maintain. If a file can be built from another one, it should be, by a machine, every time.
  • Separate gates from advice. Block only what is objectively wrong; report the rest. People keep contributing when the checks are fair.
  • Keep the core boring. Bash and plain YAML are still the right tools after thirteen years, and that is a big part of why the project is still alive.