Skip to content

About

Kotlin DSL ANSI Output Colored Console

Topics

Resources

Stars

36 stars

Watchers

1 watching

Forks

Repository files navigation

Colored Console

CI Latest release Maven Central Kotlin License: MIT

A small Kotlin DSL for printing colored and styled text to the terminal using ANSI escape codes.

All colors in normal, bright and background variants, and the text styles

Features

  • Text styles: bold, faint, italic, underline, strike, blink, reverse, hidden
  • Colors: black, red, green, yellow, blue, purple (also magenta), cyan, white, and gray
  • Bright and background colors, plus 256-color and true-color (RGB) support
  • Two-color gradients
  • Reusable custom styles
  • Conditional and nested styling
  • Styling can be switched off with a single flag
  • Helpers to strip escape codes and measure the visible length of styled text
  • Kotlin Multiplatform: tested on the JVM, JavaScript (Node.js), WebAssembly and Kotlin/Native (Linux and Windows)
  • Available on Maven Central, or as a single source file with no dependencies to copy into your project

Requirements

The output relies on ANSI escape codes, so it needs a terminal that supports them. Linux and macOS terminals work. Windows has not been tested yet: Windows 10 and later understand ANSI escape codes, but the console interprets them only when virtual terminal processing is turned on for it.

Installation

The library is on Maven Central. With Gradle, add the dependency to build.gradle.kts:

dependencies {
    implementation("io.github.marcelmatula:colored-console:1.4.0")
}

In a Kotlin Multiplatform project, add it to commonMain.dependencies { }. It is published for the JVM, JavaScript, WebAssembly (wasmJs, wasmWasi) and Kotlin/Native (linuxX64, mingwX64).

With Maven, add the JVM artifact, colored-console-jvm, to pom.xml:

<dependency>
    <groupId>io.github.marcelmatula</groupId>
    <artifactId>colored-console-jvm</artifactId>
    <version>1.4.0</version>
</dependency>

The JVM library runs on Java 8 or later. Your project needs Kotlin 2.3 or later on the JVM, and Kotlin 2.4 or later on the other platforms.

Copying the file

The library is also a single file, which works with older Kotlin versions and on platforms the dependency doesn't cover. Copy ColoredConsole.kt into your project. Keep the license notice at the top of the file; the MIT License requires it in every copy.

Imports

Import the functions you need. With a copied file, this is only needed in other packages:

import com.github.mm.coloredconsole.ColoredConsole
import com.github.mm.coloredconsole.colored
import com.github.mm.coloredconsole.print
import com.github.mm.coloredconsole.println
import com.github.mm.coloredconsole.stripAnsi
import com.github.mm.coloredconsole.style
import com.github.mm.coloredconsole.visibleLength

Usage

Colors and styles are properties you chain onto a value. They are available in two ways:

  • println { … } and print { … } print the text that the block returns.
  • colored { … } makes the styles available for a whole block of code and returns the block's result.

Both of these print the same output:

println { "Hello world".cyan.bold }

colored {
    println("Hello world".cyan.bold)
}

Output: "Hello world" in bold cyan, printed twice

The examples below use whichever form is shorter. Every example works with both.

Colors and styles

Chain as many colors and styles as you need. They work on any value, not just strings; the value is converted with toString().

val pi = 22f / 7
println { pi.blue.italic.underline }

Output: 3.142857 in blue, italic and underlined

Background and bright colors

.bg turns the most recently applied color into a background color, so you can combine it with a text color:

println { "Hello world".black.cyan.bg }

Output: "Hello world" in black on a cyan background

.bright switches the most recently applied color to its bright variant:

println { "bright blue".blue.bright.bold }

Output: "bright blue" in bold bright blue

Other styles can come in between: "text".cyan.bold.bg is the same as "text".cyan.bg.bold. Combine both for a bright background, in either order: .bright.bg or .bg.bright. The same works for custom styles, so (green + underline).bright is the same as green.bright + underline.

256 colors and true color

color256(n) picks one of the 256 colors of the extended palette (0 to 255), and rgb(r, g, b) any 24-bit color. They work like the named colors: on any value, as styles, and with .bg. They need a terminal that supports 256 or 24-bit colors.

println { "256 colors".color256(208) + " and " + "true color".rgb(95, 135, 255).bold }
println { " on a background ".black.rgb(255, 215, 95).bg }

Output: "256 colors" in orange and "true color" in bold light blue, then black text on a yellow background

Gradients

gradient(from, to) blends the text color from one color to another across the visible characters. The two colors can be rgb(…), color256(…) or named colors; named colors use the standard xterm values, which can differ from your terminal's theme. A gradient works like any color: as a style (gradient(…) + bold) and with .bg for a background gradient. It needs a terminal with true-color support.

println { "Hello from Colored Console!".gradient(rgb(255, 95, 135), rgb(95, 175, 255)).bold }
println { "  as a background gradient  ".black.gradient(rgb(95, 215, 175), rgb(175, 135, 255)).bg }

Output: "Hello from Colored Console!" in bold, blending from pink to light blue, then black text on a background blending from green to purple

Custom styles

Combine colors and styles with + and store the result to reuse it. Apply it by calling the text with the style, or with .style(…):

val header = style { green + underline + bold }

println { "Hello world"(header) }
println { "Hello world".style(header) }

Output: "Hello world" in bold, underlined green, printed twice

Inside a colored { } block, you don't need the style { } wrapper. Chaining with . works the same as +, so green.underline.bold is equivalent to green + underline + bold:

colored {
    val header = green.underline.bold
    val brightHeader = green.bright + underline + bold
    println("Hello world"(header))
    println("Hello world"(brightHeader))
}

Output: "Hello world" in bold, underlined green, then in bold, underlined bright green

Conditional styling

Pass a condition to apply a color or style only when the condition is true. The condition receives the value being styled:

// Prints the even numbers in cyan
println { listOf(1, 2, 3, 4, 5).joinToString { it.cyan { n -> n % 2 == 0 } } }

Output: 1, 2, 3, 4, 5 with 2 and 4 in cyan

Custom styles accept a condition too:

colored {
    val chapter = cyan + underline + bold
    val chapterNumber = 12
    println("$chapterNumber. Goodbye World"(chapter) { chapterNumber >= 10 })
}

Output: "12. Goodbye World" in bold, underlined cyan

Nested styling

Styles can be nested. Inner text keeps the outer styles and adds its own, and the outer styles continue after it:

colored {
    val inner = ("color " + "Yellow".yellow.bold + " normal").faint
    val middle = ("italic " + inner + " italic").italic
    println(("bold " + middle + " bold").bold)
}

Output: nested bold, italic, faint and yellow text

Turning styling off

Pass false to print plain text without any escape codes, for example when the terminal does not support colors:

println(colored = true) { "Orange".yellow.bold + " Is the New " + "Black".bold.reverse }

Output: "Orange" in bold yellow and "Black" in bold reverse video

colored(enabled = false) {
    println("Orange".yellow.bold + " Is the New " + "Black".bold.reverse)
}

Output: "Orange Is the New Black" as plain text

The library does not decide this by itself: checking the environment is platform-specific, and the library is a single file of common Kotlin code. To turn styling off automatically, compute the flag in your program. For example, on the JVM, style the output only when the program runs in a terminal, not when its input or output is redirected (to a pipe or a file), and never when the NO_COLOR environment variable is set to a non-empty value:

val useColors = System.console()?.isTerminal == true && System.getenv("NO_COLOR").isNullOrEmpty()

colored(enabled = useColors) {
    println("Orange".yellow.bold + " Is the New " + "Black".bold.reverse)
}

Console.isTerminal() needs Java 22 or later. On older versions, check System.console() != null instead.

Styling inside a class

A class that implements ColoredConsole can use colors and styles in all of its methods, without a colored { } block:

class Weather(val degrees: Int) : ColoredConsole {
    fun display() = println("Degrees:".blue.bold + " $degrees".italic.bold)
}

Weather(22).display()

Output: "Degrees:" in bold blue followed by 22 in bold italic

Measuring and stripping styled text

Escape codes count toward a string's length, which breaks alignment. visibleLength counts only the characters the text shows:

colored {
    val steps = listOf("build" to "OK".green, "tests" to "FAILED".red.bold, "deploy" to "SKIPPED".faint)
    for ((step, status) in steps) {
        println(status + " ".repeat(10 - status.visibleLength) + step)
    }
}

Output: three aligned rows, "OK" in green, "FAILED" in bold red and "SKIPPED" faint, each followed by its step

stripAnsi() removes all escape codes, for example to write the same text to a log file:

val message = colored { "Error:".red.bold + " disk full" }
println(message.stripAnsi())

Output: "Error: disk full" as plain text

Both work on any string, also outside colored { }. visibleLength counts characters, not terminal columns, so text with wide characters such as emoji or CJK may not line up exactly.

Development

Build the project and run the tests with:

./gradlew check

The build uses a Java 25 toolchain, which Gradle downloads if it is not installed. The first build also downloads Node.js, for the JavaScript and WebAssembly tests, and the Kotlin/Native compiler. Native tests run only on their own system: linuxX64 on Linux and mingwX64 on Windows. On macOS, the native targets are compiled but not tested.

The images in this README are generated from the real output of the examples, and the tests compare that output with the expected escape codes. After changing an example (in this README and in ReadmeImages.kt), update its expected output in ReadmeExamplesTest.kt and regenerate the images with:

./gradlew readmeImages

CI fails when an image is out of date.

License

Colored Console is licensed under the MIT License.

About

Kotlin DSL ANSI Output Colored Console

Topics

Resources

Stars

36 stars

Watchers

1 watching

Forks

Releases

Packages

Contributors

Languages