Description
@effect/cli annotates "Weak" spans (e.g. argument names in --help output) with Ansi.black without specifying a background color. On dark/black terminal backgrounds (a very common default), the resulting \x1b[0;30;1m sequence renders as black-on-black and is completely unreadable.
NO_COLOR=1 (https://no-color.org) does not disable the styling either.
Versions
@effect/cli@0.75.1
@effect/printer-ansi@0.49.0
effect@3.21.x
- Node.js 20.x
Reproduction
import { Args, Command } from "@effect/cli";
import { NodeContext, NodeRuntime } from "@effect/platform-node";
import { Console, Effect } from "effect";
const name = Args.text({ name: "name" });
const cmd = Command.make("hello", { name }, ({ name }) => Console.log(name));
const cli = Command.run(cmd, { name: "hello", version: "1.0.0" });
cli(process.argv).pipe(Effect.provide(NodeContext.layer), NodeRuntime.runMain);
Run with --help on a terminal with a black background:
$ node app.js --help | cat -v
...
^[[0;1m^[[0;30;1m<name>^[[0;1m^[[0m
The 30 (black foreground) escape makes <name> invisible.
Root cause
packages/cli/src/internal/helpDoc/span.ts (mirrored at dist/esm/internal/helpDoc/span.js:130) maps the Weak span variant to Ansi.black:
case "Weak": {
return Doc.annotate(toAnsiDoc(self.value), Ansi.black);
}
Pure black foreground without a background guarantee is not safe across themes. Most CLIs use blackBright (bright black ≈ dim gray) or dim/faint to indicate de-emphasized text.
Suggested fix
Either:
- Replace
Ansi.black with Ansi.blackBright (or Ansi.dim) for the Weak variant — same intent, theme-safe.
- Honor
NO_COLOR / FORCE_COLOR env vars at the renderer level so users can opt out.
Option (1) is a one-line change and would fix the immediate readability bug. Option (2) is the broader correct behavior.
Happy to send a PR if the maintainers prefer one of these approaches.
Description
@effect/cliannotates "Weak" spans (e.g. argument names in--helpoutput) withAnsi.blackwithout specifying a background color. On dark/black terminal backgrounds (a very common default), the resulting\x1b[0;30;1msequence renders as black-on-black and is completely unreadable.NO_COLOR=1(https://no-color.org) does not disable the styling either.Versions
@effect/cli@0.75.1@effect/printer-ansi@0.49.0effect@3.21.xReproduction
Run with
--helpon a terminal with a black background:The
30(black foreground) escape makes<name>invisible.Root cause
packages/cli/src/internal/helpDoc/span.ts(mirrored atdist/esm/internal/helpDoc/span.js:130) maps theWeakspan variant toAnsi.black:Pure black foreground without a background guarantee is not safe across themes. Most CLIs use
blackBright(bright black ≈ dim gray) ordim/faintto indicate de-emphasized text.Suggested fix
Either:
Ansi.blackwithAnsi.blackBright(orAnsi.dim) for theWeakvariant — same intent, theme-safe.NO_COLOR/FORCE_COLORenv vars at the renderer level so users can opt out.Option (1) is a one-line change and would fix the immediate readability bug. Option (2) is the broader correct behavior.
Happy to send a PR if the maintainers prefer one of these approaches.