A small Kotlin DSL for printing colored and styled text to the terminal using ANSI escape codes.
- Text styles:
bold,faint,italic,underline,strike,blink,reverse,hidden - Colors:
black,red,green,yellow,blue,purple(alsomagenta),cyan,white, andgray - 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
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.
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.
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.
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.visibleLengthColors and styles are properties you chain onto a value. They are available in two ways:
println { … }andprint { … }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)
}The examples below use whichever form is shorter. Every example works with both.
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 }.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 }.bright switches the most recently applied color to its bright variant:
println { "bright blue".blue.bright.bold }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.
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 }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 }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) }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))
}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 } } }Custom styles accept a condition too:
colored {
val chapter = cyan + underline + bold
val chapterNumber = 12
println("$chapterNumber. Goodbye World"(chapter) { chapterNumber >= 10 })
}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)
}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 }colored(enabled = false) {
println("Orange".yellow.bold + " Is the New " + "Black".bold.reverse)
}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.
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()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)
}
}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())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.
Build the project and run the tests with:
./gradlew checkThe 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 readmeImagesCI fails when an image is out of date.
Colored Console is licensed under the MIT License.