Skip to content

Terminal Styling ​

Theme-aware terminal styling with a chalk-compatible chainable API. Part of @silvery/ansi — use it for CLI output that respects terminal color capabilities and resolves semantic theme tokens.

Quick Start ​

typescript
import { style, createStyle } from "@silvery/ansi"

// Global pre-configured instance
style.bold.red("Error!")
style.primary("Deploy")

// Create your own
const s = createStyle()
s.hex("#818cf8")("Indigo")
s.bgYellow.black("Warning")

// With theme — Sterling tokens (silvery 0.20+)
const themed = createStyle({ theme })
themed["fg-accent"]("Deploy") // resolves $fg-accent from theme
themed["fg-error"]("Failed!")

createStyle() ​

Returns a chainable, callable style object.

typescript
import { createStyle } from "@silvery/ansi"

const s = createStyle()
s.bold.red("Error!") // bold red text
s.hex("#818cf8")("Indigo") // truecolor foreground
s.bgYellow.black("Warning") // black text on yellow background

Options ​

typescript
interface StyleOptions {
  /** Color level override. Auto-detected from terminal if omitted. */
  level?: "truecolor" | "256" | "ansi16" | null
  /** Theme object for $token resolution. */
  theme?: ThemeLike
}
OptionTypeDefaultDescription
level"truecolor" | "256" | "ansi16" | nullauto-detectedColor support level. null disables all color.
themeThemeLikeundefinedTheme object for token resolution

When level is omitted, createStyle() auto-detects from process.stdout, respecting NO_COLOR and FORCE_COLOR environment variables.

createPlainStyle() ​

Creates a style object without a theme. Equivalent to createStyle() without a theme option.

typescript
import { createPlainStyle } from "@silvery/ansi"

const s = createPlainStyle() // auto-detect color level
const s = createPlainStyle("ansi16") // force ANSI 16

Global style ​

A pre-configured singleton with auto-detected color level and no theme. Available immediately:

typescript
import { style } from "@silvery/ansi"

style.bold("Important")
style.red("Error")
style.primary("Deploy") // falls back to yellow (no theme)

Chainable API ​

Every property returns a new Style that can be chained further or called with text to apply:

typescript
const s = createStyle()

// Single modifier
s.bold("text") // bold
s.dim("text") // dim

// Chained modifiers
s.bold.italic("text") // bold + italic
s.bold.underline.red("text") // bold + underline + red

Modifiers ​

PropertySGR OpenSGR CloseDescription
bold122Bold intensity
dim222Faint/dim
italic323Italic
underline424Underline
inverse727Swap fg/bg
hidden828Hidden text
strikethrough929Strikethrough

Foreground Colors ​

Standard ANSI foreground colors:

PropertyCodeColor
black30Black
red31Red
green32Green
yellow33Yellow
blue34Blue
magenta35Magenta
cyan36Cyan
white37White
blackBright90Bright black
gray / grey90Gray (alias)
redBright91Bright red
greenBright92Bright green
yellowBright93Bright yellow
blueBright94Bright blue
magentaBright95Bright magenta
cyanBright96Bright cyan
whiteBright97Bright white

Background Colors ​

Same names prefixed with bg:

typescript
s.bgRed("text")
s.bgBlueBright("text")
s.bgBlack.whiteBright("text")

Available: bgBlack, bgRed, bgGreen, bgYellow, bgBlue, bgMagenta, bgCyan, bgWhite, bgBlackBright, bgRedBright, bgGreenBright, bgYellowBright, bgBlueBright, bgMagentaBright, bgCyanBright, bgWhiteBright.

Color Methods ​

For arbitrary colors beyond the 16 ANSI palette:

typescript
// Hex colors (foreground and background)
s.hex("#ff6347")("Tomato")
s.bgHex("#1e1e2e")("Dark bg")

// RGB values (foreground and background)
s.rgb(255, 99, 71)("Tomato")
s.bgRgb(30, 30, 46)("Dark bg")

// ANSI 256 color index
s.ansi256(196)("Bright red")
s.bgAnsi256(17)("Navy bg")
MethodSignatureDescription
hex(color: string) => StyleForeground from hex (#rrggbb)
bgHex(color: string) => StyleBackground from hex
rgb(r, g, b) => StyleForeground from RGB values
bgRgb(r, g, b) => StyleBackground from RGB values
ansi256(code: number) => StyleForeground from 256-color index
bgAnsi256(code: number) => StyleBackground from 256-color index

Theme Token Resolution ​

When a theme is provided to createStyle(), Sterling $tokens resolve to their theme colors. Both kebab and camelCase forms work.

typescript
import { createStyle } from "@silvery/ansi"
import { defaultDarkTheme } from "silvery/theme"

const s = createStyle({ theme: defaultDarkTheme })

s["fg-accent"]("Deploy") // resolves theme["fg-accent"] -> hex -> ANSI
s["fg-error"]("Failed!") // resolves theme["fg-error"]  -> hex -> ANSI
s["fg-success"]("Passed")
s["fg-muted"]("(3 files)")
s["fg-warning"]("Caution")
s["fg-info"]("Note")
s["fg-link"]("https://...") // resolves theme["fg-link"] + adds underline
s.bold["fg-accent"]("DEPLOY") // chain modifiers with tokens

Available Tokens ​

The $token resolver works on Sterling's flat hyphen-keys. The most common ones for CLI output:

TokenWithout theme (fallback)With theme
fg-accentyellow (ANSI 33)theme["fg-accent"] as hex
fg-errorred (ANSI 31)theme["fg-error"] as hex
fg-warningyellow (ANSI 33)theme["fg-warning"] as hex
fg-successgreen (ANSI 32)theme["fg-success"] as hex
fg-infocyan (ANSI 36)theme["fg-info"] as hex
fg-muteddim (SGR 2)theme["fg-muted"] as hex
fg-linkblue + underline (ANSI 34)theme["fg-link"] as hex + underline
border-defaultgray (ANSI 90)theme["border-default"] as hex
fgterminal default fgtheme.fg as hex
bgterminal default bgtheme.bg as hex

For the complete flat-token list (selection, inverse, surface stack, status fills, accent state variants, etc.) see the Sterling primer. When no theme is provided, tokens fall back to standard ANSI codes.

resolve() ​

Programmatically resolve a token to its hex value:

typescript
const s = createStyle({ theme })

s.resolve("fg-accent") // theme["fg-accent"]
s.resolve("$fg-accent") // same — $ prefix also accepted
s.resolve("$color0") // theme.palette[0]
s.resolve("$bg-surface-raised") // theme["bg-surface-raised"]

Color Level Detection and Degradation ​

createStyle() auto-detects the terminal's color capability and degrades gracefully:

LevelCapabilityHex/RGB handling
"truecolor"16 million colors38;2;R;G;B — exact color
"256"256 colors38;5;N — nearest in 6x6x6 cube
"ansi16"16 colors30--37 / 90--97 — nearest ANSI
nullNo colorAll styling stripped, plain text returned

The degradation from truecolor to 256 uses the 6x6x6 color cube (indices 16--231) and the 24-shade gray ramp (indices 232--255). The degradation from 256 to basic uses Euclidean distance in RGB space against the standard ANSI 16 color values.

Forcing a Color Level ​

typescript
// Force truecolor regardless of terminal
const s = createStyle({ level: "truecolor" })

// Force no color (useful for tests or file output)
const s = createStyle({ level: null })

// Force ANSI 16 for maximum compatibility
const s = createStyle({ level: "ansi16" })

Chalk-compatible level Property ​

The level property on Style instances is mutable and uses chalk's numeric convention:

typescript
const s = createStyle()
s.level // 0=none, 1=basic, 2=256, 3=truecolor

s.level = 0 // disable color
s.level = 3 // force truecolor

Setting level affects all chains derived from the same createStyle() call.

ThemeLike Interface ​

The theme option accepts any object with string-valued properties. It does not require the full Theme type:

typescript
interface ThemeLike {
  palette?: string[]
}

This means you can pass a partial theme or any plain object:

typescript
const s = createStyle({
  theme: {
    "fg-accent": "#818cf8",
    "fg-error": "#f7768e",
    "fg-success": "#9ece6a",
  } as any, // or a real Sterling Theme
})

s["fg-accent"]("Styled") // uses #818cf8
s["fg-warning"]("Warn") // falls back to yellow (ANSI 33) — not in theme

Template Literal Support ​

Style functions accept template literals:

typescript
const name = "world"
s.bold`Hello, ${name}!` // bold "Hello, world!"
s.red`Error: ${code}` // red text with interpolation

Examples ​

CLI Progress Output ​

typescript
import { style } from "@silvery/ansi"

console.log(style.bold("Building..."))
console.log(style.green("  ✓ Compiled 42 files"))
console.log(style.yellow("  ⚠ 3 warnings"))
console.log(style.red("  ✗ 1 error"))
console.log(style.dim("  Duration: 1.2s"))

Theme-aware Status Bar ​

typescript
import { createStyle } from "@silvery/ansi"
import { defaultDarkTheme } from "silvery/theme"

const s = createStyle({ theme: defaultDarkTheme })

function statusLine(branch: string, files: number, errors: number) {
  const parts = [
    s["fg-accent"](` ${branch} `),
    s["fg-muted"](` ${files} files`),
    errors > 0 ? s["fg-error"](` ${errors} errors`) : s["fg-success"](" clean"),
  ]
  return parts.join(s["fg-muted"](" | "))
}