Do more. Type less. Colorize different.
coloredstrings is a small utility for expressive terminal colors and text styles. It exposes a fluent, chainable API for styling strings and can act as a drop-in replacement for similar packages like yachalk.
Designed to be suitable, useful, and "batteries-included".
Example:
import coloredstrings as cs
print(cs.bold.underline.red("Error:"), "Something went wrong.")
print(cs.blue.bold("Info:"), "Everything is OK")
print(cs.italic.green("Success!"))
- No dependencies
- Composing styles in a chainable way:
black.on.white.bold("What's up?")
- Nested colors and no nested styling bug
- Support for 16-color, 256-color, and 24-bit (truecolor / RGB / hex) modes
- Auto-detection of terminal color capabilities
- Automatically fallback to the nearest supported color if the requested color isn't supported
- Friendly autocomplete API
- Call any of named true colors as a method:
aqua
,pink
and so on - Extend default styles with user-defined ones
- Strip ANSI escape codes with
strip_ansi
- Friendly to CLI arguments:
--color
&--no-color
- Support for common envs:
FORCE_COLOR
,NO_COLOR
,CLICOLOR_FORCE
&CLICOLOR
- Curious how coloredstrings compares to other libraries? See Migrating from other libraries
Stable release from PyPI:
pip install coloredstrings
Latest development version:
pip install git+https://github.com/samedit66/coloredstrings.git
Run the bundled demo:
python -m coloredstrings
Features:
import coloredstrings as cs
print(cs.bold.underline.red("Error:"), "Something went wrong.")
print(cs.blue.bold("Info:"), "Everything is OK")
print(cs.italic.green("Success!"))
# styles accepts multiple arguments and a `sep` argument like `print`:
print(cs.green("That's", "great!"))
print(cs.blue(1, 3.14, True, sep=", "))
# Nesting and combining styles:
print(cs.red(f"Hello {cs.underline.on.blue('world')}!"))
print(
cs.green(
"I am a green line "
+ cs.blue.underline.bold("with a blue substring")
+ " that becomes green again!"
)
)
# 24-bit RGB / hex and 256-color:
print(cs.rgb(123, 45, 200)("custom"))
print(cs.rgb("#aabbcc")("hex is also supported"))
print(cs.rgb("purple")("as well as named colors too"))
print(cs.rgb((169, 169, 169))("tuples can also be used"))
print(cs.color256(37)("256-color example"))
# Define theme helpers:
error = cs.bold.red
warning = cs.rgb("#FFA500")
print(error("Error!"))
print(warning("Warning!"))
# Or extend with your own styles:
bootstrap = cs.extend(
primary="blue", # may be a color / style name
secondary=(169, 169, 169), # RGB-tuple color
success=cs.green, # or any `StyleBuilder` instance
)
print(bootstrap.primary("Click me!"))
print(bootstrap.italic.secondary("You can combine builtin styles with your own!"))
print(bootstrap.success("Complete."))
Import coloredstrings
module directly:
import coloredstrings as cs
print(cs.green.on.pink("green text on pink background"))
Or use only needed styles:
from coloredstrings import white, red, blue
print(white.bold("white bold text"))
print(red("just red text"))
print(blue.strikethrough("blue strikethrough text"))
Chainable API allows you to easily compose styles and use them. When passing final text to a style, you can pass multiple objects which will be turned to strings and joined using an optional sep
argument (which defaults to a single space):
import coloredstrings as cs
print(cs.orange("text", 1, 1.0, True, sep="-"))
Although you use cs
everywhere, the actual work is done by an immutable StyleBuilder
class under the hood. Because every style object is immutable, creating a new style from an existing one doesn't modify the original. This avoids accidental cross-contamination of styles present in yachalk
:
from yachalk import chalk
import coloredstrings as cs
# With yachalk
s1 = chalk.italic
s2 = s1.red
print(s1("Chalk, am I red?"))
print(s2("Yes, you are!"))
print("-" * 8)
# With coloredstrings
s3 = cs.italic
s4 = s3.red
print(s3("Style, am I still red?"))
print(s4("Sure not, but I am!"))
In this example, s1/s2
and s3/s4
behave differently: s1/s2
are actually the same style, while s3/s4
are truly independent styles.
coloredstrings
— like yachalk
and several other libraries — is built around chaining styles. Unlike some libraries, it does not provide separate background helpers such as bg_blue
. Instead, use the on
helper to mark that the next color in the chain should be a background color. This gives you explicit control over whether the color you add applies to the foreground or the background.
Example:
import coloredstrings as cs
# Red text on a blue background
print(cs.red.on.blue("Hey!"))
# Don't write code like this - it's hard to read!
# It's equivalent to `cs.white.on.black(...)` but much less clear
print(cs.white.on.on.black("Do not write code like that."))
# Green background with default foreground
print(cs.on.green("Text on a green background"))
A few important gotchas:
-
If you chain multiple foreground colors, only the last foreground color takes effect:
print(cs.red.green.blue("Blue text")) # result: blue foreground
-
on
affects only the next color in the chain. For example:print(cs.on.magenta.cyan("Cyan text on magenta background"))
Here
magenta
becomes the background (because ofon
) andcyan
is the foreground. -
Repeated calls to
on
without an intervening color are redundant and hurt readability; prefer the simpler, clearer form.
coloredstrings
tries its best to detect terminal color capabilities automatically (see coloredstrings.color_support.detect_color_support()
), but detection can occasionally miss. You can explicitly set the color mode using the pseudo-style method color_mode(mode)
.
mode
is a member of the coloredstrings.ColorMode
enum with these values:
ColorMode.NO_COLORS
- disable styling; no escape sequences are emittedColorMode.ANSI_16
- 16 basic ANSI colorsColorMode.EXTENDED_256
- 256 color modeColorMode.TRUE_COLOR
- 24-bit RGB / truecolor support
Example:
from coloredstrings import style, ColorMode
# Notice `style`? It's a default style which does nothing.
# Force no colors
just_text = style.color_mode(ColorMode.NO_COLORS)
print(just_text.red("It isn't red"))
# Force truecolor
rgb_default = style.color_mode(ColorMode.TRUE_COLOR)
print(rgb_default.hex("#ca7e8d")("Hi!"))
With a wide variety of options to force terminal color or not, coloredstrings
respects common environment conventions (in order of precedence - higher precedence goes first):
-
FORCE_COLOR
: if set, this variable can be used to force color output even when detection would otherwise disable it (for example, when output is being piped). Following values are supported:FORCE_COLOR<=0
- same asColorMode.NO_COLOR
orNO_COLOR
environment variableFORCE_COLOR=1
- same asColorMode.ANSI_16
FORCE_COLOR=2
- same asColorMode.EXTENDED_256
FORCE_COLOR>=3
- same asColorMode.TRUE_COLOR
-
NO_COLOR
: if this environment variable is present (with any value other than an empty string), coloredstrings will avoid emitting color escape sequences. This is the community-standard way for users to opt out of colored output. -
CLICOLOR_FORCE
: same asFORCE_COLOR
. -
CLICOLOR
: same asColorMode.ANSI_16
.
You can still programmatically override detection by calling style.color_mode(...)
as shown above.
Note
CLI arguments take precedence over any environment variable.
You can also specify command-line flags like --no-color
to disable colors and --color
to enable them.
Example with a file cool.py
:
import coloredstrings as cs
print(cs.red(f"Hello {style.blue('world')}!"))
# Run with python
python cool.py --no-color
# Run with uv
uv run cool.py --color
Many terminals do not support full truecolor (ColorMode.TRUE_COLOR
). When a requested color cannot be represented in the current color mode, coloredstrings
automatically maps the requested color into the best available color space and emits the closest supported color. In short: you will still get colored output, though the result may be an approximation of the original color.
reset
- Reset the current style chain. Widely supported.bold
- Make the text bold (increases weight). Widely supported.dim
(aliases:faint
,dark
) - Render the text with lower intensity / brightness. Support varies.italic
- Render text in italic. Support varies across terminals.underline
- Draw a horizontal line below the text. Support varies.double_underline
- Draw a double underline under the text. Not widely supported.overline
- Draw a horizontal line above the text. Not widely supported.inverse
(alias:reverse
) - Swap foreground and background colors (invert colors).hidden
(alias:concealed
) - Do not display the text (it is still present in the output stream).strike
(alias:strikethrough
) - Draw a horizontal line through the center of the text. Support varies.blink
(alias:slow_blink
) - Make the text blink. Often unsupported in modern terminals; avoid depending on it.rapid_blink
- Faster blink. Often unsupported in modern terminals; avoid depending on it.framed
- Draw a frame around the text. Rarely supported.encircle
(alias:circle
) - Draw a circle/encircle the text. Rarely supported.visible
- Show text only when a color mode is enabled (anything other thanColorMode.NO_COLOR
). Mainly used for cosmetic things.
Note on attributes: Most attributes stack (they combine instead of overriding). Terminal support for many of these attributes is spotty - prefer basic attributes (
bold
,underline
,inverse
) for portability.
black
red
green
yellow
blue
magenta
cyan
white
bright_black
(aliases:gray
,grey
)bright_red
bright_green
bright_yellow
bright_blue
bright_magenta
bright_cyan
bright_white
color256(index)
- 256 colorrgb(r, g, b)
,rgb(hex)
,rgb(color_name)
- 24-bit RGB color
When you call cs
with a method not defined above, it tries to interpret the method name as a named color.
This allows having many color methods without the need to define them explicitly:
import coloredstrings as cs
from coloredstrings import purple
print(cs.lavender("`lavender` is not defined internally"))
print(purple("Neither is `purple`."))
If you’ve used other Python color or formatting libraries before, coloredstrings
will feel familiar but more robust and consistent. Below is a quick comparison of how it differs from popular alternatives:
- colorama provides low-level ANSI control and Windows compatibility but lacks a fluent API.
coloredstrings
supports more colors, styles and requires no use of ANSI codes directly.- Example:
# colorama from colorama import Fore, Style print(Fore.RED + 'Error' + Style.RESET_ALL) # coloredstrings import coloredstrings as cs print(cs.red('Error'))
- termcolor focuses on basic named colors but doesn’t support chaining or RGB.
coloredstrings
supports truecolor, background colors, attributes, and chaining.- Example:
# termcolor from termcolor import colored print(colored('Warning!', 'yellow', attrs=['bold'])) # coloredstrings import coloredstrings as cs print(cs.bold.yellow('Warning!'))
coloredstrings
lacks nested styling bug presented in termcolor:# termcolor from termcolor import colored print(colored(f"Red text with {colored('underlined', attrs=['underline'])} part", "red")) # coloredstrings import coloredstrings as cs print(cs.red(f"Red text with {cs.underline('underlined')} part"))
- yachalk inspired
coloredstrings
, but its mutable style builders can cause side effects. coloredstrings
’sStyleBuilder
is immutable, ensuring no cross-contamination between styles.- Chain syntax and API are nearly identical expect that you don't need to remember a separate method for background coloring.
- Example:
# yachalk from yachalk import chalk print(chalk.blue.bg_red.bold("Hello world!")) # coloredstrings import coloredstrings as cs print(cs.blue.on.red.blod("Hello world!"))
- rich is a full-featured library for terminal formatting, tables, markdown, and logging.
- It’s excellent for large applications but too heavy for simple coloring.
coloredstrings
aims to be minimal, dependency-free, and Pythonic for everyday terminal styling.- Example:
# rich from rich.console import Console Console().print('[bold red]Error[/bold red] Something went wrong') # coloredstrings import coloredstrings as cs print(cs.bold.red('Error:'), 'Something went wrong')
In short:
Library | No dependencies | Chainable | Truecolor | Immutable Styles | No nested styling bug | Focus |
---|---|---|---|---|---|---|
colorama | ✅ | ❌ | ❌ | ❌ | ✅ | Compatibility |
termcolor | ✅ | ❌ | ❌ | ❌ | ❌ | Simplicity |
yachalk | ✅ | ✅ | ✅ | ❌ | ✅ | Modern styling |
rich | ❌ | ❌ | ✅ | ✅ | ✅ | Full-featured UI |
coloredstrings | ✅ | ✅ | ✅ | ✅ | ✅ | Lightweight styling |
I’d love your help to make coloredstrings even better!
- 💡 Got an idea or found a bug? Open an issue and let’s talk about it
- 🔧 Want to improve the code? PRs are always welcome! Please include tests for any new behavior.
- ♻️ Try to keep changes backward-compatible where possible
- 🎨 Adding new styles or helpers? Don’t forget to update the README and include tests to ensure ANSI - sequences open and close correctly
- ⭐ If you like this project, consider giving it a star - it really helps others discover it!