How (not) to build terminal color schemes
While working on the terminal, I often end up with barely readable text like this:
I tried to find out why that happens. This article is a summary of what I found.
Modern terminals can display 256 colors. But when talking about terminal color schemes, we talk about a set of 16 colors, known as ANSI colors.
Before 16 colors, there were 8 colors: black, red, green, yellow, blue, magenta, cyan, and white. The other 8 colors were added as bright variants of these. Actually, there even are two additional colors: The default background and foreground/text color.
This set is defined in ECMA-48 (which has also been adopted as ISO/IEC 6429). Unfortunately, the standard has not much to say about the colors apart from their names. The only additional information it gives is this:
The usable combinations of parameter values are determined by the implementation.
So no wonder we end up with barely readable text: The standard authors didn't bother to define which colors should be used together.
So let's see how popular terminal themes handle this.
The Tango Desktop Project was an initiative by freedesktop.org to produce consistent design guidelines for open source desktops. Apart from a set of icons they also produced a terminal color scheme that I happen to use.
I don't think they used a systematic approach for the color scheme, especially not with regards to contrast. For example, the bright magenta is actually less bright than regular yellow.
I am used to this theme and would like to stick with it. It is listed here as some kind of baseline. Later in the article, I will try to improve it using concepts from other themes.
Solarized is one of the most popular color schemes among coders. If you want to be cool, use solarized. But beneath the hype are actually some interesting features:
First off, it uses the same CIELAB luminance values (50/60) for all of its colors to provide consistent lightness and contrast. Using CIELAB is a really good idea that I will absolutely stick with.
Second, it tries to provide both a dark and a light mode by only switching background and foreground colors. This one I find problematic: The colors do have the same contrast to black and white. But that contrast is only about 4.5, which is… ok-ish by WCAG standards.
But this low contrast is actually the third feature: The theme reduces contrast even further by using greys instead of full black and white for background and foreground. The claim is that this is easier on the eyes.
Again, I am not convinced. When I am in a well-lit place I also like lower contrast. But I can easily control that by reducing the contrast of my laptop screen. In other circumstances I need all the contrast I can get. Why should I artificially restrict the maximum contrast in the theme?
The fourth and final feature is that solarized is not only a terminal theme. It also comes with themes for many applications. This way it solves the question of which colors can be used together: By controlling both the available colors and the way that applications use them gives them full control. This of course is a massive effort and exactly the kind of work that standards should help to avoid.
Solarized has some quirks. I have no clue why, but they chose to replace most of the bright colors with greys.
So solarized is a mixed bag. I like its structured approach but don't agree with most of the design decisions.
Gruvbox is another color scheme that provides both a dark and a light mode. It actually cites solarized as one of its inspirations.
It is an interesting theme because its interpretation of the light mode is very different from that of solarized. Back in the day, all terminals had a black background. So terminals with light background have even less history to build on.
One thing that is interesting about gruvbox' light mode is that the bright colors are actually darker, not brighter, than the regular ones. In dark mode, the bright colors are both brighter and have more contrast to the background. In light mode, you have to pick one of these features. While solarized went with the former, gruvbox decided to go with the latter.
The second very interesting decision is to switch black and white: In dark mode, "black" is both black and contrasts well with the foreground. In light mode, you again have to pick one, and the two themes again took different approaches.
Which of these approaches is the better one? Unfortunately, there is no answer to that. The important thing would have been to decide on one and stick with it so everyone knows what to do. Unfortunately, the standard failed to do that. So now there is no way for application authors to know which of these approach is used. So basically, applications cannot use the black and white colors.
Another thing that is interesting about black and white is that gruvbox uses the "black" color as background and the "bright white" color as foreground. That is not the case for solarized and yet another reason why application authors cannot rely on these colors.
Tango with consistent lightness
Luckily, I am not trying to build a light theme, so many of the issues mentioned above don't directly affect me. But still, the only tangible advice I have found so far is to use CIELAB to provide consistent lightness.
Then again, it is also interesting to look at the non-features of the themes: None of them cared about contrast between regular and bright colors, and they mostly did not care about contrast to the foreground. So apparently, the only really important thing is to have sufficient contrast to the background.
That last finding is actually surprising: Some applications actually use colors in combination with the foreground. I think a reasonable compromise is to have sufficient contrast between foreground and regular colors and don't care to much about bright colors.
But that leaves us in the same spot as those themes that try to provide both a dark and a light mode: By trying to achieve sufficient contrast to both black and white, we end up with mediocre contrast to both.
Anyway, I tried the solarized approach and used CIELAB values of 50 for regular and 60 for bright colors:
The result is fine, but had lost much of the character of the original, especially in the bright colors: The red and green had lost saturation and yellow and cyan felt totally different.
I also realized that the consistent lightness might actually not be such a good idea: The goal of using colors in the first place is to help you distinguish different parts of the text. As humans are most receptive to lightness, using different lightness levels helps a lot with that distinction.
So I ended up using consistent lightness for the regular colors and carefully adjusting the bright colors:
Honestly, I am not sure I found out anything. But I will try to cobble together some advice:
For theme authors:
- Choose regular colors to have sufficient contrast to both background and foreground.
- Use CIELAB to get consistent lightness.
- Don't try to provide a theme for every application. That's too much work.
For application authors:
- Avoid black and white, as they are used inconsistently.
- Never combine two colors or a bright color with foreground. Only combine regular/bright colors with background and maybe regular colors with foreground.
That being said, I am not sure if my changes to tango actually improved anything.
Do you remember the screenshot in the beginning of this article? The one I took as motivation for this whole thing? After taking it I realized that I had forgotten to switch back to the original tango theme. So it was taken using the already improved version. That's how little has improved.
One more fun idea
This is an idea I had that didn't really fit into the article:
The bright colors were meant as highlights, and highlights can be done with things other than brightness. So what if, instead of increasing brightness, we increased saturation?
This might be a good idea for light themes, because neither brightness nor contrast have to be sacrificed to achieve the highlights. Not sure how well this works in practice though.