Question

Ruby Terminal Text Colorization with ANSI Escape Codes

rubycolorsconsole-application

Question

Using Ruby, how can I apply foreground and background colors to text printed in a terminal? Is there built-in support in Ruby’s core library for terminal colorization? If not, what is an idiomatic way to create a Ruby equivalent of Pascal’s textcolor(...) procedure?

Short Answer

You will learn how terminals interpret ANSI escape codes, how to apply foreground and background colors in Ruby, why resetting styles matters, and how to build a small reusable helper that avoids coloring redirected output.

Concept

Most modern terminals understand ANSI escape codes: short control sequences embedded in a string that instruct the terminal to change text formatting.

For example, \e[31m switches the foreground color to red. The terminal keeps using that color for subsequent output until it receives a reset sequence such as \e[0m.

Ruby does not provide a high-level, built-in textcolor method in its core library. However, Ruby strings can contain ANSI sequences directly, which makes a small helper module straightforward.

ANSI coloring is useful for command-line programs because it can make errors, warnings, success messages, headings, and status information easier to scan. It is presentation metadata for a terminal, not part of the underlying text itself.

A program should generally emit colors only when its output is connected to an interactive terminal. If output is redirected to a file or another program, raw escape sequences are usually unwanted.

Mental Model

Think of the terminal as a printer with a current ink and paper setting.

  • An ANSI code such as \e[32m tells the printer: “use green ink now.”
  • The following text is printed using that setting.
  • \e[0m tells the printer: “restore the normal settings.”

The important detail is that a color code changes a mode. It does not color only one word automatically. Your Ruby code must reset the mode after the text you intend to style.

Take Quiz

Syntax and Examples

ANSI color sequences use the general form:

"\e[<code>m"

\e is Ruby’s escape character. A color sequence starts with \e[, contains one or more numeric codes, and ends with m.

RESET = "\e[0m"
RED = "\e[31m"
WHITE_ON_BLUE = "\e[37;44m"

puts "#{RED}This message is red#{RESET}"
puts "#{WHITE_ON_BLUE}White text on a blue background#{RESET}"

Common codes:

StyleANSI code
Reset all styles0
Bold1
Black foreground30
Red foreground31
Green foreground32
Yellow foreground33
Blue foreground34
Magenta foreground35
Cyan foreground36
White foreground37
Black background40
Red background41
Green background42
Yellow background43
Blue background44

A reusable helper is clearer than scattering escape codes throughout an application:

module TerminalStyle
  RESET = "\e[0m"

  COLORS = {
    black: 30,
    red: 31,
    green: 32,
    yellow: 33,
    blue: 34,
    magenta: 35,
    cyan: 36,
    white: 37
  }.freeze

  def self.color(text, foreground: nil, background: nil, bold: false)
    codes = []
    codes << 1 if bold
    codes << COLORS.fetch(foreground) if foreground
    codes << (COLORS.fetch(background) + 10) if background

    return text.to_s if codes.empty?

    "\e[#{codes.join(";")}m#{text}#{RESET}"
  end
end

puts TerminalStyle.color("Saved successfully", foreground: :green, bold: true)
puts TerminalStyle.color("Warning", foreground: :white, background: :red)

Here, foreground: :green produces code 32. Background codes are ten higher than their foreground equivalents, so red background is 31 + 10, or 41.

Step by Step Execution

Consider this small example:

red = "\e[31m"
reset = "\e[0m"
message = "Connection failed"

puts "#{red}#{message}#{reset}"
puts "Try again."

Execution trace:

  1. red is assigned the ANSI sequence \e[31m. Nothing is printed yet.
  2. reset is assigned \e[0m, which returns terminal formatting to its default.
  3. message stores ordinary text.
  4. The first puts builds one string in this order:
    • switch to red,
    • print Connection failed,
    • reset formatting.
  5. The terminal displays Connection failed in red.
  6. The second puts prints Try again. using the normal terminal color because the previous line reset the style.

Without the reset sequence, Try again. might also appear red.

Real World Use Cases

Terminal colorization is commonly used to communicate meaning quickly:

  • CLI validation: show invalid input or failed checks in red.
  • Deployment scripts: show completed steps in green and warnings in yellow.
  • Test runners: distinguish passing, failing, and skipped tests.
  • Migration tools: highlight actions that may modify or delete data.
  • Progress/status output: make section headers, environment names, and summaries easier to find.
  • Developer scripts: visually separate log levels such as INFO, WARN, and ERROR.

For example:

puts "\e[32mPASS\e[0m User can sign in"
puts "\e[33mWARN\e[0m Missing optional API token"
puts "\e[31mFAIL\e[0m Database connection refused"
Take Quiz

Real Codebase Usage

In production command-line applications, developers usually centralize styling and make it optional.

Use a single styling interface

Avoid placing literal strings such as "\e[31m" throughout business logic. A module, formatter object, or logging layer keeps colors consistent.

Color only terminal output

$stdout.tty? is true when standard output is an interactive terminal. It is usually false for commands such as:

ruby report.rb > report.txt

A practical helper can disable colors when output is redirected or when the user sets the NO_COLOR environment variable:

module TerminalStyle
  RESET = "\e[0m"
  RED = 31
  GREEN = 32

  def self.enabled?
    $stdout.tty? && ENV["NO_COLOR"].nil?
  end

  def self.wrap(text, code)
    return text.to_s unless enabled?

    "\e[#{code}m#{text}#{RESET}"
  end

  def self.success(text)
    wrap(text, GREEN)
  end

  def self.error(text)
    wrap(text, RED)
  end
end

Use established libraries when formatting grows

For small scripts, ANSI helpers are enough. For larger CLI applications, a library can provide readable APIs and support for features such as nested styles or terminal capability handling. One common option is the colorize gem:

require "colorize"

puts "Saved".green
puts "Danger".white.on_red

Whether you use a gem or a helper, keep color decisions at the user-interface boundary rather than mixing them into data processing or domain logic.

Common Mistakes

Forgetting to reset the style

Broken example:

puts "\e[31mError"
puts "This may also be red"

The terminal stays in red mode after the first line. Always end a styled section with \e[0m:

puts "\e[31mError\e[0m"

Using colors in redirected output

ruby app.rb > output.txt

If the program always outputs ANSI codes, output.txt can contain unreadable control sequences. Check $stdout.tty? or offer a --color / --no-color option.

Assuming every output destination supports ANSI codes

Many modern terminals support ANSI sequences, but plain files, some CI logs, and non-terminal consumers may not display them as colors. Provide plain-text output as a fallback.

Using puts inside a formatting method

This makes the method harder to reuse because it cannot compose strings.

# Less reusable

def red(text)
  puts "\e[31m#{text}\e[0m"
end

Prefer returning a string:

def red(text)
  "\e[31m#{text}\e[0m"
end

puts red("Error")

Trusting unvalidated color names

If a helper accepts symbols such as :purple but does not define it, it should fail clearly or choose a documented fallback. Hash#fetch is useful because it raises an error for unknown keys.

Coloring everything

Too many colors reduce their usefulness. Reserve styling for information that needs emphasis, such as errors, warnings, success states, and headings.

Comparisons

ApproachBest forAdvantagesTrade-offs
Direct ANSI stringsTiny scripts and learningNo dependency; shows how terminals workCan become repetitive and hard to maintain
A custom Ruby helper moduleSmall-to-medium CLI toolsConsistent names; easy to disable colorsYou maintain the supported styles
A color library such as colorizeReadable application outputExpressive methods such as .red and .on_blueAdds a dependency
Plain text onlyLogs, files, machine-readable outputMaximum compatibilityLess visual emphasis in a terminal

Foreground and background colors have different jobs:

TypeExample codeEffect
Foreground\e[31mChanges the text color
Background\e[41mChanges the area behind the text
Combined\e[1;37;41mBold white text on red background
Reset\e[0mRestores normal formatting
Take Quiz

Cheat Sheet

# Basic structure
"\e[CODEmtext\e[0m"

# Common constants
RESET  = "\e[0m"
BOLD   = "\e[1m"
RED    = "\e[31m"
GREEN  = "\e[32m"
YELLOW = "\e[33m"
BLUE   = "\e[34m"

# Backgrounds
RED_BG  = "\e[41m"
BLUE_BG = "\e[44m"

# Examples
puts "#{GREEN}Success#{RESET}"
puts "#{BOLD}\e[37;41mCritical error#{RESET}"

Rules to remember:

  • Start a style with \e[ followed by numeric code(s) and m.
  • Separate multiple codes with semicolons: \e[1;31m.
  • End every styled region with \e[0m.
  • Prefer a helper method or module over repeating raw codes.
  • Use $stdout.tty? to avoid styling redirected output.
  • Respect NO_COLOR when building user-facing command-line tools.

FAQ

Does Ruby have a built-in method to color terminal text?

Ruby core does not provide a high-level terminal color API such as textcolor. You can use ANSI escape sequences directly or use a gem.

What does \e[31m mean in Ruby?

\e is the escape character. The complete sequence \e[31m is an ANSI instruction that selects red foreground text.

Why do I need \e[0m after colored text?

ANSI styles remain active until changed. \e[0m resets colors and other formatting so later output is not accidentally styled.

How do I set both foreground and background colors?

Place both numeric codes in one sequence, separated by semicolons:

puts "\e[37;44mWhite on blue\e[0m"

How can I prevent ANSI codes from appearing in a file?

Only enable colors when $stdout.tty? is true, and provide a way to disable them, such as honoring ENV["NO_COLOR"].

Is it okay to use a Ruby gem for colors?

Yes. A gem such as colorize can make code more readable. For a small dependency-free script, a short helper module is often sufficient.

Can ANSI codes do more than colors?

Yes. Common terminals also support styles such as bold, dim, underline, and inverse video. Support can vary, so use such styling sparingly.

Take Quiz

Mini Project

Description

Build a small command-line status reporter for a backup task. The program prints success, warning, and error messages with colors when it runs in a terminal, but produces clean text when its output is redirected to a file.

Goal

Create a reusable Ruby formatter that prints color-coded status messages safely.

Requirements

Use ANSI escape codes for green success messages, yellow warnings, and red errors. Only emit color codes when standard output is a terminal. Disable colors when the NO_COLOR environment variable is set. Reset terminal formatting after every styled message. Print one example message for each status level.

Take Quiz

Keep learning

Related questions

View more