Settings

Theme

How to choose colors for your CLI applications (2023)

blog.xoria.org

191 points by kruuuder 12 days ago · 102 comments

Reader

layer8 12 days ago

As long as CLI programs stick to the 8 or 16 standard colors and refrain from setting background colors (inverse mode is fine), as well as from explicitly setting white or black as text color, everyone can reasonably configure their terminal colors so that everything is readable.

When going beyond that, the colors really need to be configurable on the application.

  • JohnLeitch 12 days ago

    > refrain from setting background colors

    That's the thing though, setting bg color opens up a lot of options, and constraining to invert is not sufficient in my opinion.

    • reactordev 12 days ago

      Sticking to the \x1b[4X background color is probably safe as that can be tweaked by terminal color palettes. It’s when you use the 256 or RGB \x1b codes that it becomes an issue. Ok for foreground.

    • layer8 12 days ago

      That’s fine, but please make the colors configurable then.

      For example like Mutt does: http://www.mutt.org/doc/manual/#color

    • Joker_vD 12 days ago

      > constraining to invert is not sufficient in my opinion

      Eh. Doing green/red text color with default background, maybe inverted works amazing for me. In fact, I'd say that every sensible colour scheme for a terminal should have as the default foreground/background colours something that is more or less contrasting against every other explicitly named colour, including black and white (I personally have #212121 on #EEEEEE).

  • Arch-TK 12 days ago

    Configurable within the application... at runtime.

    I want to be able to switch existing terminals with existing applications between themes.

j4cobgarby 12 days ago

Use only default (white/black), red for bad, green for good. If you need more than that, like vim or whatever, then maybe a 'fullscreen' TUI is better, with a specified background and foreground. For CLI tools, I'm not sure if I prefer more colours.

The CSS to make the terminals look like iTerm was smooth, to the point I read them as screenshots.

  • BeetleB 12 days ago

    Hard disagree on the red/green. Use whatever you think appropriate and make it user configurable.

    • mrob 12 days ago

      It's a CLI app, it's already configurable. Every good terminal emulator lets you set custom palettes.

      • kps 12 days ago

        >Every good terminal emulator lets you set custom palettes

        Not differently for each program's output.

        • mrob 12 days ago

          Which is a good reason to stick with the de-facto standard of red for bad and green for good.

          • sceptic123 12 days ago

            unless you're colour blind

            • mrob 12 days ago

              If you're color blind, you change the palette in your terminal emulator so "red" and "green" become different colors you can distinguish. It even works for rarer forms of color blindness. This works best when people follow the de-facto standard.

            • skydhash 12 days ago

              Red here does not mean #ff0000. it means color 1. in the 4 bit colors palette

              • hulitu 10 days ago

                > Red here does not mean #ff0000

                For you maybe.

                "Look it is not gray on gray is black #777777 on white #333333". /s

          • BeetleB 12 days ago

            If for something unrelated to good/bad looks good, I'm using red. Ditto for green.

            Sure, if it was a status indicator and I used red for "good", I can see the point. But over the last few years I've had too many people tell me "Don't use red, people will think something is wrong" for things not semantically tied to good/bad.

            People wear red clothes. They buy red cars. They eat red food. They date red heads. Red is OK.

  • busterarm 12 days ago

    > red for bad, green for good

    8% of men of Northern European descent (and 0.4% of women) are red-green colorblind. That'd be a terrible choice. Use blue-orange, blue-red, or purple-green.

    • Etheryte 12 days ago

      This approach is worse. Use red and green like everyone else and the user can choose their terminal color palette to differentiate in a way that works for them. Then it works the same across all commands. If you're the odd one out, you're adding more mental overhead for the user, not less.

    • account42 12 days ago

      You are ignoring that most people already have a cultural understanding of the colors red and green. Changes done for accessibility should never making things worse for the average user.

    • skydhash 12 days ago

      Red/green is semantic in these cases. They’re user configurable in almost all terminals, so there’s no real accessibility issue. I tend to associate blue with decorative accent, yellow with info/warning text, and cyan and magenta for really fancy stuff.

      • tczMUFlmoNk 12 days ago

        Red/green has no inherent semantics. It has the semantics that you assign it. If you choose to assign it meaning that disenfranchises 8% of men using your system, that's your choice, but it is not a good one.

        • skydhash 12 days ago

          Cultural semantics (diff tools, build tools,…: green/addition/ok, red/removal/error). And people with color blindness can alter the colors to something they can differentiate. And in the ansi sequences, they are actually numbers.

        • mrob 12 days ago

          The standard terminal palette is only 16 colors. Even if you compress them all into the green-to-blue color range, it's still possible to distinguish all 16. The user can change "red" and "green" to whatever they like in the terminal preferences and then every 16-color app will be accessible with no additional effort from anybody.

    • makapuf 12 days ago

      More importantly, dont use color as sole source of information. Strikethrough, emoji or ok / bad can also be used.

      • xenophonf 12 days ago

        Emojis aren't 7-bit clean. They're hard to type. They don't mean things the same way words do. `foo | grep -i error` communicates intent better than `foo | grep :-/` or whatever goofy hieroglyph someone chose instead of, like, a word with clearly defined meaning.

        • wredcoll 11 days ago

          In my experience with live codebases, "error" or "warning" rarely mean the same thing to the same person, but admittedly you're much more likely to guess that they're in use as opposed to crying-green-clown emoji

        • craftkiller 12 days ago

          > They're hard to type

          I'd like to recommend rofimoji. I have it bound to a hotkey, so whenever I want to type an emoji, I just hit that hotkey and then a window pops up with my most recent emoji already visible at the top. Then I start typing in words that describe the emoji that I want like "crying" and it filters the list. Finally I select one and it pastes it into whatever text box I had selected before I hit the hotkey. My only complaint is I wish it worked for all unicode codepoints instead of just the emoji.

        • makapuf 12 days ago

          Yes that's why I also mentioned text labels. (strikethrough ansi codes aren't also fun to type). Besides, where are you needing 7but clean data ? Isn't that a narrow use case ?

          • xenophonf 5 days ago

            > narrow use case?

            It's the robustness principle. "Be conservative in what you do, be liberal in what you accept from others." A CLI author shouldn't assume support for UTF-8.

        • makapuf 5 days ago

          ok in that context use error or ok, just dont use color as ~10% of ppl have an issue with seeing colors perfectly (that includes people with epaper displays)

        • Lammy 12 days ago

          > They're hard to type.

          Globe key + E on Mac, Windows key + period on Windows, Ctrl + period on GNOME, Super key + period on KDE, yada yada.

  • fassssst 12 days ago

    Color is cultural. Red is associated with good in China

    • altcognito 12 days ago

      Context here matters, red finds its way into Chinese forbidden or warning signs quite often.

  • red_admiral 12 days ago

    Eh, LS_COLORS is sometimes useful once the meanings are in your subconscious.

tolciho 12 days ago

A confused user once stopped by, they had a blank terminal, so I showed them how to select all which revealed the helpfully black on black text. These days I compile colour support out of st, or set *colorMode:false for xterm. "But you can customize the colours" is a typical response, to which one might respond that one has grown weary of pushing that particular rock, and moreover one may be busy with other things at a drag-out monitor in a server room at three in the morning that has helpfully dark blue text on a black console, or worse if some high-minded expert has gone and rubbed the backside of a unicorn everywhere so that they may improve the "legibility".

s_dev 12 days ago

Colourful terminals are so useful. I have mine colour coded according to the working directory depending on the project. So I can see which terminal is associated with which project even if there are twenty terminals open. The scripts are even in my servers so when I ssh in to them it changes colour as well.

https://michael.mior.ca/blog/coloured-ssh-terminals/

red_admiral 12 days ago

There's an ever more basic rule: don't just make your text white (ANSI 37m) because you assume the terminal will have a dark background. Even white-on-black (37;40m), while usually readable, can stand out the wrong way if you assume that everyone is using dark mode.

  • account42 12 days ago

    IMO if your terminal theme does not provide high contrast for "white" text on the default or "black" backgrounds, that's for you to fix. If you want a light terminal then change the color scheme to map "black" to a bright color and "white" to a dark color while making sure that other colors have good contrast to your "black". Don't just change the default foreground and background color and expect every single color using program to fix your mess.

ori_b 12 days ago

As long as you respect the NO_COLOR variable, it will work for me.

https://no-color.org/

  • wredcoll 11 days ago

    That's a funny example. I have no issues with the idea of course but in my day to day life I'm way more likely to encounter an issue with colors being lost after sent to a pager or a log file or tee or what not

jammcq 12 days ago

I'm a bit color blind and it might be quite common to show errors in red but when the background is black, I can't see it at all.

  • bradrn 12 days ago

    Neither can I. Luckily tweaking the colours can make it somewhat readable. (Sometimes…)

joshka 12 days ago

I made the "Aardvark Blue" a while back[1] to solve some of the problems that most color schemes have. The goal of this theme is that:

- colors are fairly natural

- background and black are distinct

- grays are naturally ordered avoiding full black

- light and dark colors are distinct from each other

- all colors look good on background, black, dark gray, gray, white

We use this for all the screenshots on https://ratatui.rs and https://github.com/ratatui/ratatui

It's available from the usual places https://iterm2colorschemes.com/, https://windowsterminalthemes.dev/?theme=Aardvark%20Blue, built in to ghostty, extension for vscode etc.

[1]: https://github.com/mbadolato/iTerm2-Color-Schemes/pull/417

  • tasuki 12 days ago

    Funny thing, those are not "the usual places" I've ever heard of, and I care somewhat about color schemes. Each of us lives in our own bubble... (my bubble is not iterm2 and not windows)

    • joshka 11 days ago

      iterm2colorschemes is a source for various other tools as it ports out to

      "Terminal, Konsole, PuTTY, Xresources, XRDB, Remmina, Termite, XFCE, Tilda, FreeBSD VT, Terminator, Kitty, MobaXterm, LXTerminal, Microsoft's Windows Terminal, Visual Studio, Alacritty, Ghostty, and many more."

      You may have used data from it without knowing about it.

      • tasuki 10 days ago

        > You may have used data from it without knowing about it.

        Plausible!

        • bado 10 days ago

          Yeah, when I created that repo, it was for me to store iterm themes that I found, and it took off and got ports to, well, pretty much every terminal . I didn't want to rename the repo since it's linked to in so many places. The main link people usually see is iterm2colorschemes.com but I do own terminalthemes.com and should probably just get around to pointing that one to the repo, too

    • ctmnt 11 days ago

      Where are some of the usual places you look?

anal_reactor 12 days ago

The problem with CLI colors is that they operate on the wrong abstraction layer. Individual program shouldn't send "this text is red" but "this text signals failure" and then terminal interprets "failure" as "red". Until this change happens (never) colors in CLI will remain a hot mess.

makapuf 12 days ago

I really think we should converge to semantic codes. By example Background is zero, standard is 7, positive / negative, highlight, colored1,2,3 .. with correct defaults, and let the user have a common 8 or 16 colors palette in the terminal for all textmode apps. Imagine having some kind of unified color themes in the terminal.

nateroling 12 days ago

I’ve bounced off of LazyGit multiple times because I never figured out how to make it play nice with a light theme terminal.

I haven’t used dark mode anything for years. I set my monitor so it’s roughly as bright, or slightly brighter than, a piece of white paper.

No more flash-bangs when some website doesn’t support dark mode.

xenophonf 12 days ago

I really wish you wouldn't. All the rinky dink colors and animations screw with the CLI output when you don't correctly detect whether the user's running the app interactively.

Keep it plain text. Regular, old, boring output is good.

  • skydhash 12 days ago

    I dislike when devs only try to detect if it’s a tty, then enable all their gimmicks without even providing a flag. Not everything is xterm-256color.

    • kps 12 days ago

      And not everything that calls itself xterm-256color is actually xterm compatible *cough* GNOME *cough*.

      • bitwize 12 days ago

        Xterm is actually a terminal emulator, and has to pass a suite of conformance tests that actually check its emulation of DEC VT series terminals.

        Most of its successors are more like "shitty xterm emulators" whose conformance tests are "do my favorite mOdErN CLI apps work".

  • bitwize 12 days ago

    I agree. So many TUIs from webshit devs don't even bother to call isatty, let alone check terminfo to see if ANSI escape codes are even valid for this terminal.

    But modern open source subscribes to Mao's Continuous Revolution Theory. Calls for some measure of stability and sanity are usually dismissed with some form of the argument "awwww, is poor diddums afwaid of a widdle change?" Or in this case, "still using vi on your ADM3A, old timer? Our software is not for you."

    • xenophonf 10 days ago

      Emacs on a Link MC5, although something doesn't like how the terminal handles flow control. I'm not sure if it's the O/S, the UART, the cable, or the terminal, but I have issues with I/O corruption. Even something a simple as a directory listing will get messed up, and on both FreeBSD and Linux, so maybe that rules out the O/S. Oh well. I'll figure it out some day.

  • davidw 12 days ago

    Yeah. "The only winning move is not to play".

  • bl4kers 12 days ago

    Big blocks of text are difficult to parse

alias_neo 12 days ago

I recently spent several evenings re-working all of my colours across all of my computers and screens; terminals, IDEs, etc. Ultimately, despite using the same tools, and always dark mode, across all of my machines, the setup for each was different.

I think it's safe to set a standard colour-set so that it's immediately usable, but beyond that, a user should be customising to their requirements.

Perception differs among people; many of the colours OP listed as unreadable, were barely an issue, bright yellow being the only one I could unequivocally agree on. Perhaps display type, configuration and colour calibration is an important factor, as well as individual perception, ambient conditions, brightness levels, contrast, and perhaps even more variables have a significant effect.

I've also learned, since adding an OLED Monitor to my desk alongside the IPS ones, that it's possible to have too much contrast; brightly coloured text alongside pixels that are literally off can be just as problematic to read at times, as low-contrast.

jph 12 days ago

If you want a quick easy way to add some colors to your own shell scripts:

    export STDOUT_COLOR_START=''
    export STDOUT_COLOR_STOP=''
    export STDERR_COLOR_START=''
    export STDERR_COLOR_STOP=''
In your shell script:

    print_stdout() {
        printf %s%s%s\\n "${STDOUT_COLOR_START:-}" "$*" "${STDOUT_COLOR_STOP:-}"
    }

    print_stderr() {
        >&2 printf %s%s%s\\n "${STDERR_COLOR_START:-}" "$*" "${STDERR_COLOR_STOP:-}"
    }
Source: https://github.com/sixarm/unix-shell-script-kit

The source also has functions for nocolor, and detecting a dumb terminal setup that doesn't use colors, etc.

  • kps 12 days ago

    1. That script's color check doesn't check that the output is a terminal. Also test

        tty -s
    
    
    2. Don't hardcode escape sequences. Use (e.g.)

        export STDOUT_COLOR_START="`tput setaf 4`".
    • jph 7 days ago

      Yes good points both, thank you. The source code link has more explanation about color choices and my preference of POSIX compatibility. You can also see the color function that checks NO_COLOR, CLICOLOR_FORCE, TERM = "dumb", -t 1, etc.

      For color operands, I use raw escape codes because I aim for POSIX compatibility. If I'm working on something that needs more capabilities then I switch to a more powerful programming language e.g. python, rust, etc.

      As far as I understand, POSIX tput defines some terminal operands (clear, init, reset, etc.) but not color operands (setaf, setab, sgr0, etc.).

  • wpm 12 days ago

    If you're writing a zsh script and not worried about portability, you can also use the prompt expansion colors with "print".

        print_color () {
          print -P "%F{$1}$2%f"
        }
    
    And then to use it

       print_color "green" "This is printed in green"
    • godelski 12 days ago

      Here's something also useful that's portable

        declare GRN='\e[1;32m'
        declare RED='\e[1;31m'
        declare YLW='\e[1;33m'
        declare CYN='\e[1;36m'
      
        write_log() {
            echo -e "[$( date +'%c' )] : ${1}\e[0m" | tee -a ${logfile}
        }
      
        write_log "${RED}I'm an ERROR"
    • leephillips 12 days ago

      Nice. I put this in my .zshrc.

  • godelski 12 days ago

    That seems needlessly cumbersome, why not

      declare STDOUT_COLOR='\e[34m'
      declare STDERR_COLOR='\e[31m'
      declare COLOR_STOP='\e[0m'
    
      print_stdout() {
          echo -e "${STDOUT_COLOR}${*}${COLOR_STOP}" &> /dev/stdout
      }
    
      print_stderrr() {
          echo -e "${STDERR_COLOR}${*}${COLOR_STOP}" &> /dev/stderr
      }
    
    Like why are you exporting? Do you really need those in your environment?

    And those print statements aren't going to work by default.

    • jph 7 days ago

      Good questions.

      For shell syntax, I aim for POSIX because for anything more advanced I switch from shell to a more powerful programming language.

      Currently POSIX doesn't have the 'declare' statement, nor the '\e' syntax consistently, nor the 'echo -e' syntax consistently.

      As for exporting, I do it because I have many quick scripts that I run often, and I prefer to switch the colors in one place such as in the evening from light mode to dark mode.

      When you say the print statements aren't going to work by default, can you say more about this? Anything you can explain or point me to about this will be a big help. Thanks!

  • direwolf20 12 days ago

    What is the purpose of making everything the same color?

thinking_cactus 12 days ago

Interesting analysis, but perhaps it warrants a different conclusion: it's almost impossible to please everyone in this case. The resulting colours seem of some utility, but if you intend to make something more interesting you're probably annoy some (potentially large) group, in the case of legacy terminal coloring.

hnsmhthrow 12 days ago

I used solarized since it came out but I dropped it some years back. I don’t think I can use it for dark mode. It’s too washed out and dull compared to light mode which is what I used to use it with. I just use whatever VS Code or VIM gives me as a dark mode and it’s usually better.

loicd 11 days ago

In addition to $TERM, I wish there was a standard variable defined by terminal emulators that would contain the background color. This would let programs choose their colors accordingly, rather than try for a one-size-fits-all.

0x457 12 days ago

Use 8 or 16 standard colors, let me configure them via terminal emulator or give me a way to completely change color scheme. That is it. Very rarely I need per-application colors in terminal (or anywhere really).

assimpleaspossi 12 days ago

Black on white has seemed to work for centuries without issue. For general reading, throw in bold and italic and you're pretty much set. For programming, black on white is still a go to color (or lack thereof).

seanwilson 12 days ago

If the goal of the post is to pick terminal colors that contrast on both white/light and black/dark backgrounds, it means you're stuck with midtone colors (between light and dark). This is really limiting for color choice (there's no such thing as "dark yellow" for example), and lowers the maximum contrast you can have for text because you get the best contrast when one color is dark and the other is light.

Ideally, instead of the CLI app switching to "bright green", it would pick a "bright contrasting green". So if the terminal background was dark, it would pick bright green, and for light background it would pick a darker green. There isn't CLI app implementations for this? This is similar to how you'd implement dark mode in a web app.

  • account42 12 days ago

    > Ideally, instead of the CLI app switching to "bright green", it would pick a "bright contrasting green". So if the terminal background was dark, it would pick bright green, and for light background it would pick a darker green. There isn't CLI app implementations for this? This is similar to how you'd implement dark mode in a web app.

    The responsibility for this lies with the color scheme not the terminal program.

  • JoshTriplett 12 days ago

    CLI apps can detect the background color of the terminal, and determine contrasting colors accordingly.

  • alt187 12 days ago

    That's called `\e[0;92m`, aka the ANSI terminal espace sequence for bright green. You have 15 others, that will be displayed however the terminal's user wants. They're already available in most terminal color libraries, too.

sroussey 12 days ago

This is true for the console in dev tools as well.

Problem there is you can’t change css so at the moment the systems color preference changes thing will look bad.

Important considerations for custom formatters.

bitwize 12 days ago

It's 2026, and app developers are solely responsible for not causing eyehurt, even if their users insist on using the Hotdog Stand theme.

munificent 12 days ago

Tangential, but I really love the design of this blog.

pimlottc 12 days ago

What is Sorcerer here?

otabdeveloper4 12 days ago

Step one: *always* assume a dark background.

lifetimerubyist 12 days ago

I use the built-in TokyoNight Day theme as my light theme in GhosTTY and I think it's almost perfect. Then I use TokyoNightMoon for dark. Works great. Hard to use anything else now.

If you're CLI application doesn't play nice with it (i haven't seen many) I don't use it.

keepamovin 12 days ago

Can you work this into an AGENTS.md ? Just so happen to be working on multiple TUI at the moment: text-based modern web browser, VPS rental console, agentic coding wrapper.

Colors, have been a perpetual nightmare.

Keyboard Shortcuts

j
Next item
k
Previous item
o / Enter
Open selected item
?
Show this help
Esc
Close modal / clear selection