omarcoaraujo/tint

Terminal string styling library for Luau

Tint

Chalk-inspired terminal string styling for Luau.

Check Release


Tint showcase in a terminal

Features

  • Fluent chainable API — tint.red.bold.underline("text")
  • Nested styles with automatic color restoration
  • True color RGB and hex support with automatic downgrading per terminal capability
  • Automatic terminal color level detection (respects NO_COLOR, FORCE_COLOR, COLORTERM, etc.)
  • Runtime agnostic — works with Zune, Lune, and Lute
  • Zero dependencies

Installation

Add Tint to your pesde.toml:

[dependencies]
tint = { name = "omarcoaraujo/tint", version = "^0.2.1" }

Then require it:

local tint = require("@pkg/tint")

Usage

Basic colors

print(tint.red("ERROR"))
print(tint.green("SUCCESS"))
print(tint.yellow("WARNING"))
print(tint.blue("INFO"))
print(tint.cyan("note"))

Modifiers

print(tint.bold("bold"))
print(tint.italic("italic"))
print(tint.underline("underline"))
print(tint.dim("dim"))
print(tint.strikethrough("strikethrough"))

Chaining

Styles can be chained in any order:

print(tint.red.bold("red and bold"))
print(tint.blue.bold.underline("important"))
print(tint.green.italic("success"))

Nesting

Outer colors are automatically restored after inner styled segments:

print(tint.red("error: ", tint.yellow("detail"), " continues in red"))
print(tint.bold("BOLD ", tint.underline("BOLD+UNDERLINE"), " BOLD again"))

True color (RGB & Hex)

print(tint.rgb(255, 136, 0)("orange"))
print(tint.hex("#ff8800")("also orange"))
print(tint.rgb(255, 136, 0).bold("bold orange"))
print(tint.bold.hex("#ff8800")("also bold orange"))

-- Background
print(tint.bg_rgb(30, 30, 46)("text on dark bg"))
print(tint.bg_hex("#1e1e2e")("text on hex bg"))

On terminals that do not support true color, RGB and hex values are automatically downgraded to the nearest 256-color or 16-color equivalent.

256-color codes

print(tint.ansi256(200)("pink"))
print(tint.bg_ansi256(200)("pink background"))

At color level 1 the codes are downgraded to the 16-color palette.

Underline styles and colors

print(tint.underline_curly("curly"))
print(tint.underline_double("double"))
print(tint.underline_dotted("dotted"))
print(tint.underline_dashed("dashed"))

print(tint.underline_curly.underline_red("red curly"))
print(tint.underline_curly.underline_bright_blue("blue curly"))
print(tint.underline_rgb(255, 136, 0)("orange underline color"))
print(tint.underline_hex("#ff8800")("same, from hex"))
print(tint.underline_ansi256(200)("256-color underline color"))

The underline color only shows when an underline style is also applied. Terminal support varies (Kitty, WezTerm, VTE, Windows Terminal, iTerm2).

Visible

visible only prints when colors are on, and returns an empty string otherwise:

print(tint.visible.dim("only with colors"))

Strip

Removes every ANSI escape code from a string:

print(tint.strip(tint.red.bold("plain")))

Multi-line text

Styles are closed and reopened around each line break, so every line keeps its style:

print(tint.bg_red("first line\nsecond line"))

Background colors

print(tint.bg_red("red background"))
print(tint.bg_green("green background"))
print(tint.red(tint.bg_white("red text on white background")))

Color level detection

Tint detects color support automatically at startup and adapts all output accordingly.

LevelDescriptionTriggered by
0No colorsNO_COLOR, TERM=dumb, pipe / non-TTY
1Basic 16 colorsMost terminals, CI environments
2256 colorsTERM=xterm-256color
3True color (16M)COLORTERM=truecolor, Windows Terminal, iTerm 3+

Fixed level

tint.new creates an independent copy locked to a level, ignoring the terminal:

local basic = tint.new({ level = 1 })
print(basic.hex("#ff8800")("always 16 colors"))

local plain = tint.new({ level = 0 })
print(plain.red("never colored"))

Without a level, the copy uses the detected one.

Overriding

FORCE_COLOR=0 zune run script.luau  # disable all colors
FORCE_COLOR=1 zune run script.luau  # exactly 16 colors, even on Windows
FORCE_COLOR=3 zune run script.luau  # force true color
NO_COLOR=1    zune run script.luau  # disable per no-color.org spec

Or via CLI flags:

zune run script.luau --no-color     # disable colors
zune run script.luau --color=256    # force 256 color mode

Runtime compatibility

Tint works across Luau runtimes with automatic detection:

RuntimeColor supportTTY detection
ZuneFullYes
LuneFullNo (assumes TTY)
LuteFullNo (assumes TTY)

License

MIT