Field Notes

Terminal colors are a contrast problem too

A terminal theme is sixteen colors of small monospace text. Most themes ship with two of them unreadable, and the light variant fails in a different place than the dark one.

  • color
  • terminal

Nobody runs a contrast checker on their terminal. Web pages get audited. Terminals get a theme someone liked on a screenshot, and then eight hours a day of reading small monospace text in it.

A terminal theme is a palette like any other: a background, a foreground, and sixteen ANSI colors that every tool draws with. Error messages are color 1. Diffs use 2 and 1. Comments and line numbers are almost always color 8, bright black. Each of those is text on the background, and each is a pair that either passes 4.5:1 or does not.

Where dark themes fail

Dark themes fail on black and bright black. Both are meant to be "dim," so they are drawn close to the background, and on a near-black surface they land around 1.2:1 and 2:1. Then the shell uses bright black for the prompt's secondary text, the editor uses it for line numbers and comments, and the diff tool uses it for context lines. The most frequently read secondary text in the whole environment is the one color nobody can see.

The Color Lab's example dark theme, drawn for the lab, shows exactly this: 15 of 17 pass, and the two that fail are colors 0 and 8. The fix in the gallery entry lifts them to a cool mid-gray that still reads as dim and clears 5:1.

Where light themes fail

Light themes fail somewhere else entirely. The bright variants, colors 9 through 15, were tuned to pop on a dark background. On a pale surface, bright yellow, bright cyan, bright green, and bright white drop below 2:1. Bright white on near-white is invisible by definition. A tool that writes a warning in bright yellow has just written it in invisible ink.

That is why a theme with light and dark variants is two palettes, not one. The same sixteen hues cannot pass on both a near-black and a near-white background. Each variant needs its own check, and the colors that fail will usually be different ones.

The 2 am test

Dim text is a design choice with a hidden assumption: a fresh pair of eyes, a good monitor, a dark room. Remove any one of those and dim becomes gone. A theme that passes 4.5:1 on every color still looks like a theme. It just keeps looking like one at 2 am on a laptop in a bright kitchen.

Check yours

The Color Lab has a Terminal Themes mode. Paste the dark variant in one box and the light variant in the other. It accepts Ghostty style palette = N=#hex lines, color0 to color15 key-value lines, or JSON with named keys like brightBlack. Each color is rendered as a sample line on its own background with the ratio and a pass or fail.

If a color fails, you have two honest fixes. Move the color toward the foreground until it passes, which is what the gallery entry does. Or move the background. What does not work is making the text bolder; bold helps legibility, but the contrast ratio is about luminance, and bold does not change it.

A note on "dim"

Many terminals also support a dim attribute that renders any color at reduced intensity. A theme that passes can still be defeated by a tool that prints in dim. If you maintain a tool, use color 8 for secondary text and let the theme decide how dim it is. If you maintain a theme, make color 8 pass, because that is where everyone's secondary text ends up.

Open the companion lab

Spotted something this note gets wrong?

Corrections and better examples are the best kind of message.

Send a correction