Color Coordination Using Base16
The Problem
A few days ago I wrote about how I moved from a all-inclusive environment (KDE Plasma) to Qtile, a tiling window manager, and all the things I did to have the amenities I missed from Plasma.
One thing there that may need further explanation is the section about color schemes because the concepts themselves are fairly unusual.
So, let's dig into it a bit more.
This is how a few apps look by default if you don't configure things:
One is a Qt app, and the other is a GTK app and they look ... bad?
I mean, I don't really care all that much, but not only are the widget styles totally different, but the colors are all over the place.
And the same happens to Qtile's bar, and my terminal (alacritty) and my tmuxer (zellij) and my web browser and so on.
Everything is a different color.
In Plasma, a comon color scheme is enforced on most apps (not in chrome, though) and it makes things much nicer.
So, what's the equivalent in the Window Manager world? There isn't one, really, but there are bits and pieces you can make into ... 80% of it.
Here's what I did.
My Solution
First, you need a source of consistent color schemes. If you just really, really, really like a single one, then they may have a site full of configurations for different apps to force them to follow it.
For example: Dracula or Nord
But I wanted to be able to try a bunch and see which one I liked.
For this, there is nothing like base16
Base16 defines a standard. You will have 16 colors. Those colors mean things.
- Color 0 is the background.
- Color 1 is the foreground.
And so on.
Then it defines some other things, like "If you are doing a 'dark' scheme, colors go from darker to lighter" and so on.
Then they provide a collection of themes, which are each 16 colors following those rules.
For example, this is the Brogrammer Theme:
scheme: "Brogrammer"
author: "Vik Ramanujam (http://github.com/piggyslasher)"
base00: "1f1f1f"
base01: "f81118"
base02: "2dc55e"
base03: "ecba0f"
base04: "2a84d2"
base05: "4e5ab7"
base06: "1081d6"
base07: "d6dbe5"
base08: "d6dbe5"
base09: "de352e"
base0A: "1dd361"
base0B: "f3bd09"
base0C: "1081d6"
base0D: "5350b9"
base0E: "0f7ddb"
base0F: "ffffff"
Now, suppose you want an app to follow the "base 16 standard" to see how it looks in Brogrammer style.
All you need to do is take that app's config file and put the right color where it needs it.
So, for example, for the Alacritty terminal:
colors:
# Default colors
primary:
background: '0x1b1918'
foreground: '0xa8a19f'
# Colors the cursor will use if `custom_cursor_colors` is true
cursor:
text: '0x1b1918'
cursor: '0xa8a19f'
# Normal colors
normal:
black: '0x1b1918'
red: '0xf22c40'
green: '0x7b9726'
yellow: '0xc38418'
blue: '0x407ee7'
magenta: '0x6666ea'
cyan: '0x3d97b8'
white: '0xa8a19f'
Of course editing all the config files every time you want to change your color scheme is a pain.
So the base16 project also collects templates. Those are files that when combined with a theme generate the configuration file for an application.
This is the Alacritty template:
# Base16 {{scheme-name}} - alacritty color config
# {{scheme-author}}
colors:
# Default colors
primary:
background: '0x{{base00-hex}}'
foreground: '0x{{base05-hex}}'
# Colors the cursor will use if `custom_cursor_colors` is true
cursor:
text: '0x{{base00-hex}}'
cursor: '0x{{base05-hex}}'
# Normal colors
normal:
black: '0x{{base00-hex}}'
red: '0x{{base08-hex}}'
green: '0x{{base0B-hex}}'
yellow: '0x{{base0A-hex}}'
blue: '0x{{base0D-hex}}'
magenta: '0x{{base0E-hex}}'
cyan: '0x{{base0C-hex}}'
white: '0x{{base05-hex}}'
See those bits like {{base05-hex}}
? That one gets replaced with your theme's color 5.
But again, using the template for each app you want to theme is boring.
So you need a tool to do that. The one I use is called flavours
What flavours does is:
- Get all the base16 color themes
- Get all the base16 app config templates
- Following a configuration file, generate all the config files you need.
- Optionally: run commands so the apps notice their configs have changed.
For example, this is my configuration for Alacritty:
[[items]]
file = "~/.config/alacritty/alacritty.yml"
template = "alacritty"
subtemplate = "default-256"
rewrite = false
It says:
- Generate
~/.config/alacritty/alacritty.yml
- Use the
alacritty
template - From that template use the version called
default-256
(don't worry) - DO NOT RECREATE THE WHOLE FILE
That last bit is important. That file doesn't just have the theme, it has a lot of other important stuff, so I don't want it to just have the theme in it.
So, I had to edit it once and put these lines before and after where the theme goes:
# Start flavours
... theme goes here
# End flavours
So when I use flavours
to apply a theme it will only replace that bit and leave the rest.
Since alacritty notices when the config has changed, I don't need a hook
entry. In other cases you can use it. Here's mine for qtile, to let it know it should reread its config:
hook = "killall -USR1 qtile"
So, I configured this for a bunch of things, and at this point, I can just run something like flavours apply atelier-forest
and ...
Yeah, they are not perfect. I still need to tweak some stuff, but it's getting there.
Currently my flavours configuration coordinates these:
- Alacritty
- Qtile
- Zellij
- Rofi
- Qt
- Gtk (using Flatcolor)
- VS Code
- QuteBrowser
That's roughly every app I use (yes, I am migrating from Chrome to QuteBrowser for no good reason)
And because I am not ok, I wrote a couple of tiny things to make things easier for me.
A rofi-based theme picker called rofi-base16:
And a base 16 template for Zellij.
I also wrote an ugly script to set VS Code's theme but it's shameful, so will not be shown here.
Hopefully this explains something to someone. If it doesn't, well, it will help me when I inevitably forget how this works :-)