tty

TTY and ANSI color-support detection for Gleam. Works on both Erlang and JavaScript targets.

Hex Docs

Install

gleam add tty

Usage

import gleam/io
import tty.{Ansi256, Basic, NoColor, Stdout, TrueColor}

pub fn main() {
  case tty.is_tty(Stdout) {
    True -> io.println("interactive!")
    False -> io.println("piped or redirected")
  }

  case tty.detect_color_level(Stdout) {
    NoColor -> io.println("plain text")
    Basic -> io.println("16 colors")
    Ansi256 -> io.println("256 colors")
    TrueColor -> io.println("24-bit color")
  }
}

Color resolution rules

detect_color_level(stream) evaluates in order (first match wins):

  1. NO_COLOR set to any non-empty value → NoColor
  2. FORCE_COLOR set (overrides the TTY check) → 0/false=NoColor, 2=Ansi256, 3=TrueColor, ""/1/true/unknown=Basic (case-insensitive)
  3. Stream is not a TTY → NoColor
  4. TERM=dumbNoColor
  5. COLORTERM is truecolor or 24bit (case-insensitive) → TrueColor
  6. TERM contains 256Ansi256
  7. CI set → Basic
  8. Default (unknown TTY with no color hints) → NoColor

The default in rule 8 errs on the side of safety: emitting ANSI escapes to a terminal that may not handle them is worse than rendering plain text. Set FORCE_COLOR=1 (or any other supported value) to opt in explicitly.

Honors the NO_COLOR standard and uses a precedence model inspired by chalk/supports-color.

Notes:

Public API guidance

For 1.x, this module intentionally exposes a small stable surface:

ColorLevel is intentionally closed for 1.x (NoColor, Basic, Ansi256, TrueColor) to keep matching behavior predictable. Compare levels with color_level_at_least or color_level_compare rather than relying on any numeric rank; the integer mapping is an internal implementation detail.

You can also gate behavior on the detected level without matching every variant:

import tty.{Ansi256, Stdout, color_level_at_least, detect_color_level}

let level = detect_color_level(Stdout)
case color_level_at_least(actual: level, at_least: Ansi256) {
  True -> render_256_color()
  False -> render_basic()
}

Runtime compatibility and fallback behavior

Supported runtimes:

In JavaScript runtimes without process/process.env (for example, browser or Worker contexts), this library degrades safely:

Requirements

Troubleshooting

For contributor setup, testing, and release workflow details, see DEV.md.

License

Dual licensed under either of

at your option.

Search Document