CLIs
There is no CSS in a terminal, but Enma's rules carry over. A CLI built this way feels like the rest of Manish's tools: calm, precise, numbers first.
The rules
- One accent color for the single most important thing: the result, or the prompt waiting for input. Everything else uses the terminal's default color.
- Secondary text is dim, not gray-colored. Use the dim attribute (
\x1b[2m) so it works on light and dark terminals. - Numbers are right-aligned in tables, with units: "1,284 files", "3.2 MB", "42s".
- Status is a word or symbol plus color, never color alone: "✔ Done", "✖ Failed", "! 3 warnings".
- Respect the environment: honour
NO_COLOR, print plain output when stdout is not a TTY, offer--jsonfor scripts, and show spinners only on a TTY. - Voice: verbs first, sentence case, the outcome, no exclamation marks: "Deployed to production in 42s".
A tiny helper
ts
// ui.ts: Enma for the terminal, no dependencies
const useColor = process.stdout.isTTY && !('NO_COLOR' in process.env);
const paint = (code: string) => (text: string) => (useColor ? `\x1b[${code}m${text}\x1b[0m` : text);
/** The one accent, per theme: Graphite's lime, Ember's amber. */
export const accent = paint('38;2;212;255;58');
export const dim = paint('2');
export const ok = (text: string) => `${paint('32')('✔')} ${text}`;
export const fail = (text: string) => `${paint('31')('✖')} ${text}`;
/** Right-aligns the last column, the way Enma tables align numbers. */
export function table(rows: [string, string][]) {
const width = Math.max(...rows.map(([label]) => label.length));
const valueWidth = Math.max(...rows.map(([, value]) => value.length));
return rows.map(([label, value]) => `${label.padEnd(width)} ${value.padStart(valueWidth)}`).join('\n');
}
ts
console.log(ok(`Deployed to ${accent('production')} in 42s`));
console.log(
table([
['Files', '1,284'],
['Size', '3.2 MB'],
['Cache hits', '96%'],
]),
);
console.log(dim('Run with --json for machine-readable output.'));
The 24-bit color is Graphite's dark accent from enma/tokens (color.graphite.dark.accent); import it instead of hard-coding when the CLI already depends on Enma.