A stylesheet author's guide to terminal colors

  Wynn Netherland • 2012-04-18

tldr; Use grc to add "stylesheets" and personal color settings for your favorite terminal apps.

Like Merlin in The Once and Future King I feel as if I'm getting younger as I advance in my development career.

When I started writing ASP Classic™ in the late 90s I was captive to the GUI. The DOS prompt held no real power and .ini files were something you fiddled with when a driver was misbehaving. Sophisticated developers of sophisticated applications, I was told, used property pages to configure their apps. These little tab-laden modal dialogs were never more than a right click away from anywhere it seemed.

When I left .NET and Windows development for Ruby on Rails in 2006, I was thrust into a *nix world where the terminal is not only embraced but celebrated. In the past few years, I've come to love text mode. Last year, after a Changelog episode on Vim, I made the switch full time to the venerated terminal editor. In the last month, I made the switch to Solarized (dark) as my system-wide color scheme and am loving it. With a pleasing palette in place, I now try to make effective use of it across my entire toolchain.

Why color is important in the terminal

Before looking at how to colorize your terminal apps, let's remind ourselves why terminal colors can be useful.

  • Contrast. When we're tailing a log or viewing a config file, often we're scanning for errors, server names, IP addresses, and other bits of important information. Color is a great way to highlight and quickly find those bits of information.
  • Meaning. Every bit of content has a hierarchy of information. Errors have more weight than warnings. Keys have a different weight than values. Depending on the context of your terminal, you can convey meaning with color. Errors and failing tests are often red, passing tests green, warnings yellow, and so forth.

Good syntax highlighting employs both of these principles. Type names, string values, and delimiters are given different weight or color treatment from the rest of the code. As with any good thing, moderation is the key to colorizing. Call out what's important, aim for readability.

Colorize your tools

Color has its uses beyond the text editor. Many popular development frameworks ship with some degree of color support. The Rails log, for instance, supports ANSI color codes out of the box thanks to the config.colorize_logging option. Foreman also provides great color support:

Color in Foreman

The git log supports color as well:

git lola

Both of these apps provide color support via inline ANSI color codes. If you're familiar with CSS, you might think of these as inline styles of sorts. Let's look at how Rails does it using ActiveSupport::LogSubscriber:

  class LogSubscriber
    # Embed in a String to clear all previous ANSI sequences.
    CLEAR   = "\e[0m"
    BOLD    = "\e[1m"

    # Colors
    BLACK   = "\e[30m"
    RED     = "\e[31m"
    GREEN   = "\e[32m"
    YELLOW  = "\e[33m"
    BLUE    = "\e[34m"
    MAGENTA = "\e[35m"
    CYAN    = "\e[36m"
    WHITE   = "\e[37m"

  ...

  protected

    ...

    def color(text, color, bold=false)
      return text unless colorize_logging
      color = self.class.const_get(color.to_s.upcase) if color.is_a?(Symbol)
      bold  = bold ? BOLD : ""
      "#{bold}#{color}#{text}#{CLEAR}"
    end
  end
end

First, a palette of color constants maps to an ANSI sequence to render that color. It's important to note here that these named color constants aren't the same as named colors in CSS. For instance red in CSS is always #ff000, but in the terminal, RED or \e[31m, is determined by the user's terminal color scheme. Solarized provides a great mapping table between terminal color names and various color spaces.

Git uses a similar palette declaration, providing support for bold text as well:

#define GIT_COLOR_NORMAL          ""
#define GIT_COLOR_RESET           "\033[m"
#define GIT_COLOR_BOLD            "\033[1m"
#define GIT_COLOR_RED             "\033[31m"
#define GIT_COLOR_GREEN           "\033[32m"
#define GIT_COLOR_YELLOW          "\033[33m"
#define GIT_COLOR_BLUE            "\033[34m"
#define GIT_COLOR_MAGENTA         "\033[35m"
#define GIT_COLOR_CYAN            "\033[36m"
#define GIT_COLOR_BOLD_RED        "\033[1;31m"
#define GIT_COLOR_BOLD_GREEN      "\033[1;32m"
#define GIT_COLOR_BOLD_YELLOW     "\033[1;33m"
#define GIT_COLOR_BOLD_BLUE       "\033[1;34m"
#define GIT_COLOR_BOLD_MAGENTA    "\033[1;35m"
#define GIT_COLOR_BOLD_CYAN       "\033[1;36m"
#define GIT_COLOR_BG_RED          "\033[41m"
#define GIT_COLOR_BG_GREEN        "\033[42m"
#define GIT_COLOR_BG_YELLOW       "\033[43m"
#define GIT_COLOR_BG_BLUE         "\033[44m"
#define GIT_COLOR_BG_MAGENTA      "\033[45m"
#define GIT_COLOR_BG_CYAN         "\033[46m"

Simply by wrapping text in these ANSI codes, programs can provide color and font weight formatting for those terminals that support it. Dig around the usage help for your various apps to see what color support might be available. Utilities like ack and most REPLs like irb provide some color support.

Color in your shell prompt

Another way color can add value to your terminal is in your shell prompt. Bash, Zsh, and other shells support color and frameworks like oh-my-zsh and bash-it make it easy to add color for things like git status, file paths, even battery meters in your shell prompt. I recently wrote about adding contextual TODO counts to mine. Here's the source for my left and right side prompts:

export PROMPT='%{$fg[blue]%}%c \
$(git_prompt_info)\
$(git_time_since_commit)%{$reset_color%} \
%{$fg[white]%}%(!.#.⚡)%{$reset_color%} '

set_prompt () {
  export RPROMPT="$(notes_prompt TODO) \
  %{$fg_bold[yellow]%}$(notes_prompt HACK)%{$reset_color%} \
  %{$fg_bold[red]%}$(notes_prompt FIXME)%{$reset_color%} \
  %{$fg_bold[white]%}$(todo_prompt +next)%{$reset_color%}"
}

precmd() {
  set_prompt
}

Using the $fg[color] commands, I can send the appropriate ANSI codes to format that bit of text. Think of the $reset_color as a closing tag, returning formatting to that of plain text. I'm pretty happy with the results.

Color in your own CLI

If, like me, you write your own shell scripts or command line interfaces, you can certainly write the ANSI sequences yourself, but there are a number of tools that make it easy to add color to your output. The Colored gem extends String to make it super simple to add color:

puts 'Roses are red'.red
puts 'Violets are blue'.blue
puts 'Colors are cool,'
puts 'And so are you.'.red_on_blue

If you need blinking text, check out cli-colorize. As always, use responsibly.

Colorize all the things with grc

While it's great that we can bring some color to our terminal by injecting some ANSI codes in this way, it virtually amounts to using inline styles in CSS. Why are inline styles a last resort for the stylesheet author? Because the goal of CSS is to separate our presentation from our markup. So how can we specify colors for our terminal apps even if it's not feasible to modify the source? Or, to stretch the metaphor, how can we create stylesheets for our terminal apps?

One way is via grc, the Generic Colouriser from Radovan Garabík. Install via your favorite package manager. I'm on OS X so I use Homebrew:

brew install grc

Once installed, grc provides two command line tools grc and grcat that will colorize darn near anything:

ping colored

Formatting rules are specified in config files. Here's a snippet from the conf.ping config file that ships with grc that highlights the IPs in the above example:

# ip number
regexp=\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}
colours=magenta
=======
# ipv6 number
regexp=(([0-9a-fA-F]{1,4})?\:\:?[0-9a-fA-F]{1,4})+
colours=magenta
=======
# time
regexp=\d+\.\d+\sms
colours=green
=======
...

The Homebrew formula ships with some helpful aliases for common commands to pipe them through grc for color. The beauty of grc, however, is that you can supply your own config files (in addition to those that come with the app). Adam Jahnke and I created a shell script to find all of our code annotations. Rather than hard-coding the color values in the script, he and I can now use grc to colorize to our own tastes. I use a git alias:

# Find FIXME, TODO, etc
n = !"git ls-files | xargs notes | awk -F: '{ print $1,$2; print $3,$4; print $5}' | grcat conf.notes "

Now when I run git n my grc "stylesheet" is applied:

# TODO
regexp=TODO
colours=cyan
count=more
======
# HACK
regexp=HACK
colours=yellow
count=more
=====
# FIXME
regexp=FIXME
colours=red
count=more
=====
# Line numbers
regexp=\s\d+$
colours=blue
count=more

... which yields a nicely colorized output.

Now, my latest dotfile discovery is spelunking for grc config files to pick up some tips and tricks. If you've got some, let me know, perhaps on Twitter or on Hacker News.

Wynn Netherland
Wynn Netherland

Engineering Director at Adobe Creative Cloud, team builder, DFW GraphQL meetup organizer, platform nerd, author, and Jesus follower.