Consistent terminal colors with 16-ANSI-color Vim themes

Instead of configuring colors separately for both Vim and the rest of the terminal, limiting Vim’s color scheme to 16 ANSI colors allows setting all color preferences in the terminal’s theme. Doing so results in consistent colors among terminal utilities and a color theme that’s quick to swap out for another one.

split.png
Figure 1: Vim and a git log side by side in tmux in Terminal.app. By using Dim as Vim’s color scheme, all color values are set by the terminal theme (showing both dark and light mode). The font is SF Mono, 14pt, with a line spacing of 1.2.

I’ve been using a customized version of Vim’s default color scheme for the last couple of years. Unlike Vim’s default, it exclusively uses 16 ANSI colors by setting each color to a value between 0 and 15. The terminal emulator’s theme sets the specific color values instead of hard coding them into the Vim color scheme and relying on true color support.

By using the terminal theme exclusively, all utilities use the same color values. For example, I don’t configure tmux’s colors, as the green color the terminal theme uses is already a shade I like.

Terminal colors and ANSI escape sequences

Terminal emulators use ANSI escape sequences to–amongst other things like controlling the cursor’s position–read the desired text and background color when printing output.

echo -e "\033[31mred\033[m" # Prints “red” in red.

Although most terminal emulators support at least 256 colors, most utilities use one of the main sixteen colors (black, red, green, yellow, blue, magenta, cyan, white, and a high intensity or bright version of each).

For example, git shows diffs with additions in green and deletions in red, and most testing frameworks print green dots for passed tests and red F​s for failures.

Normal Bright
Black 0 8
Red 1 9
Green 2 10
Yellow 3 11
Blue 4 12
Purple 5 13
Cyan 6 14
White 7 15

The terminal emulators we use today assign each of these relative named colors with a 24-bit color value. This separates the utility’s intent (“print this error in red”) from the terminal emulator’s styling (“#C31633 is a nice shade of red”). These colors are configurable in your terminal emulator’s settings, along with the used font and other options.

Terminal themes

By changing the terminal theme, using one like appsignal.terminal or any of the hundreds of other available themes, the terminal emulator can display the relative colors in any true color value you like. Using #C31633 for red will color deletions in git’s diff output in that exact color, for example.

picker.png

Tip: In Terminal.app, use ⌘+I to open the Inspector to change the current window’s theme without opening a new terminal window.

Setting the precise color values in the terminal emulator’s profile or using a terminal theme allows the utility to mark some output as “yellow”, without having to supply an exact shade. The shade is either up to the terminal or the user’s preferences.

Vim color schemes

Unlike other utilities, Vim uses 8-bit or 24-bit color schemes that use absolute colors for each highlight group in an attempt to make the colors look the same for every configuration. These schemes don‘t use any of the named colors, so the terminal’s color preferences don‘t affect them.

When using Vim inside a terminal emulator, having consistent colors between Vim and the rest of the terminal requires duplicate color settings. First, the Vim color scheme uses an absolute color value for each highlight group. Then, the terminal emulator needs a theme that uses the same colors as the Vim scheme to customize the relative colors.

If the Vim color scheme uses #C31633 as a red color, the terminal theme needs to use that color as the “Red” ANSI color to use that same color in git’s diffs outside of Vim, for example.

Dim: a 16-color Vim color scheme

To achieve consistent colors across all terminal utilities without requiring duplicate color settings, 4-bit Vim color schemes like Noctu and Dim exclusively use the first sixteen relative ANSI colors. This makes them compatible with 16-color terminals, but also results in consistent colors between terminal utilities.

Dim is based on Vim’s default color scheme. Although Vim’s default mostly uses relative colors, it uses 8-bit absolute colors for some of its highlight groups. By default, it colors line numbers and statements brown, diffs lightblue and darkblue, completion menus pink, and comments ANSI blue. Because that’s distracting, Dim uses gray colors for each of these.

compare.png
Figure 2: Vim’s default color scheme (left) and Dim, both with appsignal.terminal as the terminal theme. Dim has dimmed colors for secondary features like completion menus, line numbers and folds.

By keeping the intent separate from the styling and the specific color values in the terminal theme, every utility in the terminal uses consistent colors. This makes changing themes—like switching between dark and light versions of a theme—a matter of pressing ⌘+I and selecting another one, without having to find a matching color scheme for Vim.