I’ve always relied on 2-bit coloring for terminal output, sticking to eight foreground and eight background colors. It’s nice because it conforms to any user’s terminal theme. But there’s a whole world of colors out there (in most modern terminals) and I figured it was about time I started expanding my horizons.

I write most of my command line tools and scripts in Ruby. Call me old fashioned, but it really is a beautiful, readable language with plenty of room for elegant solutions. So this little snippet is in Ruby, but the core of it is simple enough that I can’t imagine it would take much work to port.

Like all terminal color output, it relies on ANSI escape codes. 2-bit escape codes look like \033[0;30;41m, which would be regular black text on a red background. But you can use RGB values in most modern terminals with an escape code like \033[38;2;RRR;GGG;BBBm (for a foreground color). So all you have to do is get RGB values for a specific color and insert them into that code.

The following Ruby snippet (gist here) will take a CSS hex color, e.g. #FA380E, and turn it into an ANSI escape sequence for you. It splits the hex value and uses the #hex method in Ruby to convert a 2-digit hex into a value between 1 and 256. With it you can run something like print "text to color".color256('#FA380E') and get some bright red text. The snippet itself is designed to be included in a script. If you run it directly you can test by using ./256color.rb "TEXT TO COLOR" "FG" "BG" where FG and BG are 3 or 6-digit hex codes. The octothorp (#) is always optional.

256color.rbraw
"
#!/usr/bin/env ruby
# frozen_string_literal: true

# 256-color hex interpretation for terminal colorization
#
# Usage
# "text".color256(foreground, background)
#
# print "Colorize this".color256('#f7921e', '#666')

# String category
module Color256
  def fg(fgc)
    "\x1b[38;2;#{fgc.rgb}m"
  end

  def bg(bgc)
    "\x1b[48;2;#{bgc.rgb}m"
  end

  def reset
    "\x1b[0m"
  end

  def rgb
    hex_string = sub(/^#?/, '')

    hex_string = hex_string.split(//).map { |a| a * 2 }.join('') if hex_string =~ /^#?[a-f0-9]{3}$/i

    parts = hex_string.match(/#?(?<r>..)(?<g>..)(?<b>..)/)
    t = []
    %w[r g b].each do |e|
      t << parts[e].hex
    end
    t.join(';')
  end

  def color256(fgc, bgc = nil)
    out = ''
    out += fg(fgc)
    out += bg(bgc) if bgc
    out += self
    out + reset
  end
end

class ::String
  include Color256
end

def test
  256.times do |r|
    256.times do |g|
      256.times do |b|
        c = ''
        [r, g, b].each { |comp| c += format('%02x', comp) }
        print ' '.color256('000', c)
      end
    end
  end
end

if __FILE__ == $0
  print ARGV[0].color256(ARGV[1], ARGV[2])
end

Hope that’s of use to somebody!

By the way, this is incorporated into the latest version of Doing such that you can use %#RRGGBB instead of a color name to set colors in a template. And use %b#RRGGBB to set a background color. Customize away!