Skip to content

Repository files navigation

coloredstrings

Python packagePyPI DownloadsPyPI versionSupported Python versionsLicenceCode style: RuffChecked with mypy

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:

importcoloredstringsascsprint(cs.bold.underline.red("Error:"), "Something went wrong.")
print(cs.blue.bold("Info:"), "Everything is OK")
print(cs.italic.green("Success!"))

preview image


Features🔥

  • 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

Installation

Stable release from PyPI:

pip install coloredstrings

Latest development version:

pip install git+https://github.com/samedit66/coloredstrings.git

Quick start

Run the bundled demo:

python -m coloredstrings

Features:

importcoloredstringsascsprint(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.redwarning=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 namesecondary=(169, 169, 169), # RGB-tuple colorsuccess=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."))

Usage

Import coloredstrings module directly:

importcoloredstringsascsprint(cs.green.on.pink("green text on pink background"))

Or use only needed styles:

fromcoloredstringsimportwhite, red, blueprint(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):

importcoloredstringsascsprint(cs.orange("text", 1, 1.0, True, sep="-"))

StyleBuilder

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:

fromyachalkimportchalkimportcoloredstringsascs# With yachalks1=chalk.italics2=s1.redprint(s1("Chalk, am I red?"))
print(s2("Yes, you are!"))
print("-"*8)
# With coloredstringss3=cs.italics4=s3.redprint(s3("Style, am I still red?"))
print(s4("Sure not, but I am!"))

yachalk bug image

In this example, s1/s2 and s3/s4 behave differently: s1/s2 are actually the same style, while s3/s4 are truly independent styles.

Chaining and gotchas

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:

importcoloredstringsascs# Red text on a blue backgroundprint(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 clearprint(cs.white.on.on.black("Do not write code like that."))
# Green background with default foregroundprint(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 of on) and cyan is the foreground.

  • Repeated calls to on without an intervening color are redundant and hurt readability; prefer the simpler, clearer form.

Supported color modes

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 emitted
  • ColorMode.ANSI_16 - 16 basic ANSI colors
  • ColorMode.EXTENDED_256 - 256 color mode
  • ColorMode.TRUE_COLOR - 24-bit RGB / truecolor support

Example:

fromcoloredstringsimportstyle, ColorMode# Notice `style`? It's a default style which does nothing.# Force no colorsjust_text=style.color_mode(ColorMode.NO_COLORS)
print(just_text.red("It isn't red"))
# Force truecolorrgb_default=style.color_mode(ColorMode.TRUE_COLOR)
print(rgb_default.hex("#ca7e8d")("Hi!"))

FORCE_COLOR, NO_COLOR, CLICOLOR_FORCE and CLICOLOR

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 as ColorMode.NO_COLOR or NO_COLOR environment variable
    • FORCE_COLOR=1 - same as ColorMode.ANSI_16
    • FORCE_COLOR=2 - same as ColorMode.EXTENDED_256
    • FORCE_COLOR>=3 - same as ColorMode.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 as FORCE_COLOR.

  • CLICOLOR: same as ColorMode.ANSI_16.

You can still programmatically override detection by calling style.color_mode(...) as shown above.

CLI arguments

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:

importcoloredstringsascsprint(cs.red(f"Hello {style.blue('world')}!"))
# Run with python
python cool.py --no-color
# Run with uv
uv run cool.py --color

Fallback behavior

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.

Styles

Attributes

  • 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 than ColorMode.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.

Colors (both foreground and background)

  • 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 color
  • rgb(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:

importcoloredstringsascsfromcoloredstringsimportpurpleprint(cs.lavender("`lavender` is not defined internally"))
print(purple("Neither is `purple`."))

Migrating from other libraries

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

  • 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:
    # coloramafromcoloramaimportFore, Styleprint(Fore.RED+'Error'+Style.RESET_ALL)
    # coloredstringsimportcoloredstringsascsprint(cs.red('Error'))

termcolor

  • termcolor focuses on basic named colors but doesn’t support chaining or RGB.
  • coloredstrings supports truecolor, background colors, attributes, and chaining.
  • Example:
    # termcolorfromtermcolorimportcoloredprint(colored('Warning!', 'yellow', attrs=['bold']))
    # coloredstringsimportcoloredstringsascsprint(cs.bold.yellow('Warning!'))
  • coloredstrings lacks nested styling bug presented in termcolor:
    # termcolorfromtermcolorimportcoloredprint(colored(f"Red text with {colored('underlined', attrs=['underline'])} part", "red"))
    # coloredstringsimportcoloredstringsascsprint(cs.red(f"Red text with {cs.underline('underlined')} part"))

yachalk

  • yachalk inspired coloredstrings, but its mutable style builders can cause side effects.
  • coloredstrings’s StyleBuilder 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:
    # yachalkfromyachalkimportchalkprint(chalk.blue.bg_red.bold("Hello world!"))
    # coloredstringsimportcoloredstringsascsprint(cs.blue.on.red.blod("Hello world!"))

rich

  • 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:
    # richfromrich.consoleimportConsoleConsole().print('[bold red]Error[/bold red] Something went wrong')
    # coloredstringsimportcoloredstringsascsprint(cs.bold.red('Error:'), 'Something went wrong')

In short:

LibraryNo dependenciesChainableTruecolorImmutable StylesNo nested styling bugFocus
coloramaCompatibility
termcolorSimplicity
yachalkModern styling
richFull-featured UI
coloredstringsLightweight styling

Contributing

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!