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[32mtells the printer: “use green ink now.” - The following text is printed using that setting.
\e[0mtells 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.
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:
| Style | ANSI code |
|---|---|
| Reset all styles | 0 |
| Bold | 1 |
| Black foreground | 30 |
| Red foreground | 31 |
| Green foreground | 32 |
| Yellow foreground | 33 |
| Blue foreground | 34 |
| Magenta foreground | 35 |
| Cyan foreground | 36 |
| White foreground | 37 |
| Black background | 40 |
| Red background | 41 |
| Green background | 42 |
| Yellow background | 43 |
| Blue background | 44 |
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:
redis assigned the ANSI sequence\e[31m. Nothing is printed yet.resetis assigned\e[0m, which returns terminal formatting to its default.messagestores ordinary text.- The first
putsbuilds one string in this order:- switch to red,
- print
Connection failed, - reset formatting.
- The terminal displays
Connection failedin red. - The second
putsprintsTry 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, andERROR.
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"
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
| Approach | Best for | Advantages | Trade-offs |
|---|---|---|---|
| Direct ANSI strings | Tiny scripts and learning | No dependency; shows how terminals work | Can become repetitive and hard to maintain |
| A custom Ruby helper module | Small-to-medium CLI tools | Consistent names; easy to disable colors | You maintain the supported styles |
A color library such as colorize | Readable application output | Expressive methods such as .red and .on_blue | Adds a dependency |
| Plain text only | Logs, files, machine-readable output | Maximum compatibility | Less visual emphasis in a terminal |
Foreground and background colors have different jobs:
| Type | Example code | Effect |
|---|---|---|
| Foreground | \e[31m | Changes the text color |
| Background | \e[41m | Changes the area behind the text |
| Combined | \e[1;37;41m | Bold white text on red background |
| Reset | \e[0m | Restores normal formatting |
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) andm. - 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_COLORwhen 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.
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.
Keep learning
Related questions
Add a Default Value to an Existing Column in a Rails Migration
Learn how to add or change a default value on an existing database column using Rails Active Record migrations safely.
Calling a Class Method from an Instance in Ruby
Learn how to call a class method from an instance in Ruby using self.class, with examples, pitfalls, and practical usage patterns.
Calling an Overridden Monkey-Patched Method in Ruby
Learn how to call the original method when monkey patching in Ruby, including alias_method patterns, examples, pitfalls, and practical usage.