Crust logoCrust

Style

Terminal styling and layout utilities for CLI output.

@crustjs/style exports ANSI-aware style functions, dynamic colors, hyperlinks, and text layout helpers. Default exports read terminal capability inputs on each call; createStyle() creates an instance with fixed settings.

Install

npm install @crustjs/style

Quick start

import { bold, fg, green, style } from "@crustjs/style";

console.log(green("OK"), bold("Build complete")); // OK Build complete
console.log(style.bold.red("Build failed")); // Build failed
console.log(fg("brand", "#4fa83d")); // brand

Named styles are callable and chainable. Chains also support tagged templates.

Styling

bold("direct");
bold`tagged ${value}`;
bold.red.bgYellow("chained");

Modifiers are bold, dim, italic, underline, inverse, hidden, and strikethrough. The package also exports the standard 16 foreground and 16 background names.

A chain is a ChainableStyleFn, which can be passed where a (text: string) => string StyleFn is expected. Null and undefined inputs to chainables produce an empty string.

Dynamic colors

fg("error", "#ff0000");
fg("ocean", "rgb(0, 128, 255)");
fg("coral", [255, 127, 80]);
style.bold.fg("rebeccapurple")("note");

fg() and bg() accept three- or six-digit hex, RGB strings, RGB tuples, and named CSS colors. Invalid input throws TypeError, including when the text is empty.

The resolved ColorDepth is "truecolor" | "256" | "16" | "none". Read style.colorDepth, colorsEnabled, or trueColorEnabled to inspect it.

Color control

import { createStyle } from "@crustjs/style";

const color = createStyle({ mode: "always" });
const plain = createStyle({ mode: "never" });
console.log(JSON.stringify(color.red("error"))); // "\u001b[31merror\u001b[39m"
console.log(JSON.stringify(plain.red("error"))); // "error"

Default exports use process.stdout.isTTY, NO_COLOR, FORCE_COLOR, COLORTERM, and TERM. FORCE_COLOR=0 and FORCE_COLOR=false disable all ANSI, while any other FORCE_COLOR value forces ANSI and overrides NO_COLOR. Without FORCE_COLOR, a non-empty NO_COLOR disables colors; forced values 1, 2, and 3 select 16-color, 256-color, and truecolor depth.

createStyle({ mode: "always" }) enables all ANSI at truecolor depth. mode: "never" returns plain text, and mode: "auto" accepts overrides for deterministic tests.

link("docs", "https://crustjs.com");
link(style.underline("Crust"), "https://crustjs.com", { id: "docs" });

Links emit OSC 8 sequences when capability checks allow them and otherwise return the label. URLs are validated even when emission is off, and NO_COLOR does not disable links.

Layout

import { link, padEnd, style as layoutStyle, table } from "@crustjs/style";

console.log(padEnd(layoutStyle.bold("Name"), 10)); // "Name      "
console.log(table(["Name", "State"], [["build", "ok"]]));
// | Name  | State |
// |-------|-------|
// | build | ok    |
console.log(link("docs", "https://crustjs.com")); // docs

padStart, padEnd, center, and stringWidth measure visible terminal columns rather than ANSI bytes. On Bun they call Bun.stringWidth; on Node and Deno a JavaScript fallback matches Bun for common terminal text (ASCII, ANSI escapes, CJK, emoji, combining marks), while exotic clusters may measure differently. table() uses headers for its column count, supports per-column alignment, and accepts readonly (as const) headers, rows, and alignments.

Reference

type ColorMode = "auto" | "always" | "never";
type ColorDepth = "truecolor" | "256" | "16" | "none";

The root exports style, createStyle, named chainables, fg, bg, link, layout helpers, and their option and input types. See the source inventory in packages/style/src/index.ts.

Prop

Type

Prop

Type

Fine print

const chain = style.bold.red;
const open = chain.open;
const close = chain.close;

open and close are raw static ANSI strings and do not recheck capabilities. Use chain(text) for capability-aware, nesting-safe output.

Direct fg(text, input) requires string text. Chainables accept other stringifiable values, return "" for nullish or empty input, and still validate dynamic color input.

On this page