Skip to content

@effect/cli: --help output uses hardcoded black foreground, unreadable on dark terminals #6200

@ncaq

Description

@ncaq

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:

  1. Replace Ansi.black with Ansi.blackBright (or Ansi.dim) for the Weak variant — same intent, theme-safe.
  2. 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.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions