Terminal Color Codes Are Messier Than You Think

Red and black in a terminal aren't just about making text look nice. They're escape sequences, they're a compatibility minefield, and the way they render varies between your terminal emulator, your operating system, and sometimes even the font you have loaded. Let me explain what these codes actually are before we get into the problems people hit. Standard red is ANSI escape code 31 — the sequence ESC[31m. Bright red is code 91 — ESC[91m. Standard black is code 30, bright black (which is really dark gray in most palettes) is code 90.

That's the surface-level stuff. The actual issues start when you realize these codes don't mean the same thing everywhere. Your color red in GNOME Terminal is not the same hex value as color red in iTerm2, and neither matches what Windows Terminal spits out. More importantly, the background matters. Red text on a dark background looks completely different from red text on white, and ANSI doesn't have a concept of relative contrast. It just has a color. Period.

The Problem With Black

Black text is basically useless on any default dark theme. This sounds obvious but people still ship tools that use black foreground with no regard for the default palette. The workaround most people don't know about: use code 90 (bright black) instead of 30 for secondary or subtle text on dark backgrounds. It renders as a dark gray in nearly every modern terminal emulator. It's not truly black, but it gives you a lower-contrast layer without breaking visibility. I spent three weeks debugging a deploy script where the error output was red on a user's machine but looked nearly invisible. Turned out they were running Alacritty with a solarized-dark theme where the standard ANSI red (code 31) maps to a desaturated red that blends into the background at standard opacity. The fix was switching the script to use bright red (code 91) and adding a reset sequence ESC[0m right after each colored block. Without that reset, the color bleeds into everything that follows.

Get the Full Details

Black and Dark Red Color Scheme - Palettes - SchemeColor.com
Black and Dark Red Color Scheme - Palettes - SchemeColor.com

The Reset Sequence Is Where Most People Fail

You have to reset. Not "it's good practice." You literally have to reset, or your subsequent output inherits whatever color you last set. I've seen production dashboards where a single missing \033[0m caused the entire footer to render in bright red because a previous error message set code 91 and never cleared it. The pattern you want is: printf '\033[31mERROR: something broke\033[0m\n'

Not this, which is what I see in way too many hobby projects: echo -e "\033[31mERROR\033[0m something broke" The difference is that printf handles escape sequences consistently across shells. echo -e is shell-dependent. In dash or sh on Debian systems, echo -e doesn't process escapes the same way. Scripts break silently. You get literal \033[31m printed to the console instead of red text, and by the time you figure out why, you've already wasted an afternoon.

True Color vs. 16-Color Palette

There's a middle ground called true color (24-bit) where you can specify any hex value directly. The sequence looks like ESC[38;2;R;G;Bm. But here's the catch: only about 60% of terminals in active use support it. If you're writing something for a general audience and you use true color red, half your users will see either the fallback palette red or garbage characters depending on their emulator. The safe path is sticking to the 16 ANSI colors and accepting that your red will vary. If you need exact color consistency, you're in design territory, not terminal territory, and you should be using a GUI framework or a proper theming system instead of raw escape codes.

25+ Best Red Color Palettes with Names and Hex Codes | Dark red color ...
25+ Best Red Color Palettes with Names and Hex Codes | Dark red color ...

Windows Is Its Own Problem

If you need this to work on Windows, ANSI support is conditional. Windows 10 (build 15063+) has it enabled by default in Windows Terminal and can be toggled in the classic Console. Windows 8 and earlier don't support it natively at all. If you're shipping a cross-platform tool, you're looking at either a conditional check for ANSI support or a library like chalk (Node) or colorama (Python) to normalize the behavior. There's no clean solution here. Every workaround adds a dependency or a code branch. Pick your poison based on your audience's OS distribution.

What I Wish I'd Known Earlier

The biggest thing I missed was that bold and color are separate attributes. Code 1 (bold) plus code 31 (red) gives you bright red in some terminals and just bold red in others. There's no guarantee. If you want consistent bright red across environments, use code 91 directly. Don't rely on bold to approximate it. Same rule applies to bright black — code 90 is your answer, not code 1 + code 30. Also worth knowing: some terminals interpret code 31 as a dim red rather than a bright one. This is a palette choice made by the terminal author. You can't override it with escape sequences alone. If contrast is critical, test on at least three different emulators before shipping anything that depends on those colors being readable.