TUI dark-theme diff foregrounds have low contrast on tinted backgrounds

Open 💬 2 comments Opened Jul 15, 2026 by chouchunming

What version of Codex CLI is running?

codex-cli 0.144.4; also reproduced against main at 365d70203da18c2a6f6848df3778e390e794ef17.

What subscription do you have?

ChatGPT authentication; the exact tier does not appear relevant to this local TUI renderer issue.

Which model were you using?

gpt-5.6-sol, but the issue is in local diff rendering and is model-independent.

What platform is your computer?

Darwin 24.5.0 arm64 arm

What terminal emulator and version are you using?

Reproduced in both Apple Terminal 455.1 and iTerm2 3.6.11 on dark terminal profiles. iTerm2 reported TERM=xterm-256color and COLORTERM=truecolor.

What issue are you seeing?

In the TUI's built-in diff cards, foregrounds on dark-theme insert/delete backgrounds can have very low contrast:

  • plain insert/delete content uses green/red foregrounds over dark green/red backgrounds;
  • syntax-highlighted delete content additionally receives Modifier::DIM, producing dim gray or muted syntax colors over the dark red background;
  • the gutter is also dimmed.

The result can be difficult to distinguish even though the terminal correctly supports 256 colors and truecolor. The same terminals render ordinary git diff --color=always foreground colors correctly.

The root cause is in codex-rs/tui/src/diff_render.rs: dark rich-color content uses red/green foregrounds, syntax-highlighted deletions are always dimmed, and dark gutters use style_gutter_dim().

This is distinct from #13012 (overly bright backgrounds, fixed by #13037) and #33422 (tmux rendering the fills as solid yellow).

What steps can reproduce the bug?

  1. Use a dark terminal profile with 256-color or truecolor support.
  2. Start Codex CLI 0.144.4.
  3. Ask Codex to modify a syntax-highlighted source file with both additions and deletions.
  4. Inspect the built-in Edited ... diff card, especially deleted syntax-highlighted lines.

What is the expected behavior?

Keep the dark red/green background cues, but render the gutter, sign, and content with a high-contrast foreground. For dark truecolor and ANSI-256 diff lines, white foreground text is substantially more readable. Light themes and ANSI-16 foreground-only behavior should remain unchanged.

Additional information

I prepared and tested a focused implementation against current main:

https://github.com/chouchunming/codex/commit/bf0c12406bd669874542ebcce33da743084e9575

The proposed change:

  • uses white foregrounds only for dark truecolor/ANSI-256 insert and delete lines;
  • removes DIM from syntax-highlighted deletions in those dark rich-color modes;
  • clears per-token syntax backgrounds so the line-level diff background remains visible;
  • preserves light-theme and ANSI-16 behavior;
  • adds coverage for plain and syntax-highlighted insert/delete lines in truecolor and ANSI-256 modes.

Validation completed:

  • just fmt
  • just fmt-check
  • just fix -p codex-tui
  • cargo test --offline --locked -p codex-tui --lib diff_render::tests:: — 52 passed, 0 failed

Per the contribution policy, I am opening the issue first and requesting confirmation of the approach before opening a PR.

View original on GitHub ↗

2 Comments

akblissweb · 1 month ago

I can reproduce this on macOS using Core Shell with the Dracula theme.

Environment:

  • Codex CLI 0.145.0
  • Core Shell 4.1
  • macOS
  • TERM=xterm-256color
  • COLORTERM=truecolor
  • no tmux

File edits/diffs in the Codex TUI can become effectively unreadable—for example, yellow text over a yellow/gold background. The terminal itself appears to be rendering the colors it receives correctly.

There seem to be two related contributors:

  1. The Dracula Terminal.app profile currently assigns the same color to ANSI yellow and bright yellow. I prepared a separate fix changing bright yellow to Dracula’s specified #FFFFA5.
  1. Codex’s dark rich-color diff rendering combines tinted red/green backgrounds with syntax-token foreground colors and DIM styling. On some themes this produces very low contrast. A more readable approach appears to be using the terminal’s default foreground over the diff background, clearing token backgrounds, and avoiding DIM for dark truecolor/ANSI-256 diffs.

I prepared a candidate implementation in my fork for discussion:
https://github.com/akblissweb/codex/tree/fix/tui-diff-contrast

I understand external pull requests are invitation-only, so I’m sharing this as reproduction details and a possible direction rather than opening an unsolicited PR.

chouchunming · 1 month ago

Thanks for putting together the reproduction and candidate implementation. I tested the same general direction and ended up with a hybrid of the theme-aware approach here and an earlier hardcoded-white experiment.

The behavior that worked best was:

if dark_rich && matches!(kind, Insert | Delete) {
    style.fg = None;
    style.bg = None;
    style = style.remove_modifier(Modifier::DIM);
} else if matches!(kind, Delete) {
    style = style.add_modifier(Modifier::DIM);
}

Plain changed content similarly uses the resolved red/green background with no explicit foreground. The +/- signs remain green/red. The changed-line gutter uses the default foreground, but unchanged context lines retain syntax colors and a dimmed gutter.

Trade-offs

Theme-default foreground (fg = None)

  • Pro: respects the terminal/theme's intended readable foreground and follows Codex's preference to avoid hardcoded white.
  • Pro: works for both TrueColor and ANSI-256 without assuming what ANSI white looks like in a user's palette.
  • Con: contrast still ultimately depends on a sane terminal theme.

Hardcoded white

  • Pro: deterministic and immediately improves many dark themes.
  • Con: can be wrong for light/custom themes, and ANSI White is a configurable palette slot rather than a guaranteed high-contrast color.

Suppressing syntax foregrounds on changed lines

  • Pro: makes diff semantics readable and consistent; syntax-token backgrounds can no longer cover the diff tint.
  • Con: changed lines intentionally lose syntax color distinctions. Unchanged context lines should therefore remain untouched.

Two correctness details in the current candidate commit

At 7610f5a61bb4bf8600af1f062eac654c758f60fd:

  1. Ratatui Style::remove_modifier and Style::add_modifier return a new Style; they do not mutate in place. The returned values need to be assigned, otherwise DIM is neither removed nor added in those branches.
  2. The dark-rich condition currently also matches DiffLineType::Context (both in syntax composition and the (DiffTheme::Dark, _, Some(...)) gutter arm). That strips syntax styling and gutter dimming from unchanged lines. Restricting the override to Insert/Delete avoids that regression.

The proposed snapshot also uses the existing text-oriented TestBackend helper, whose golden output contains characters but not foreground/background/modifier state; the commit does not include an accepted .snap file. A style-structure snapshot (assert_debug_snapshot! on the rendered RtLines) catches this regression directly.

Test matrix used for the hybrid

On current main at 62fd410384:

  • RED confirmed the old behavior: plain dark-rich content had explicit Green/Red foregrounds and syntax content retained its RGB foreground.
  • Dark TrueColor and ANSI-256:
  • plain Insert/Delete: default foreground, resolved diff background retained;
  • syntax Insert/Delete: foreground/background cleared, DIM removed, unrelated modifiers preserved;
  • + remains green and - remains red;
  • changed gutters use the default foreground.
  • Context: syntax foreground/background/modifiers and dimmed gutter are preserved.
  • Light TrueColor and dark ANSI-16 delete syntax retain the existing DIM behavior.
  • A style-aware snapshot records all of the above.
  • just test -p codex-tui: 3,254 passed, 4 skipped.
  • just fix -p codex-tui: clean.
  • just fmt: clean.
  • Fresh arm64 Codex binary built successfully.
  • Manual visual check passed in a fresh Codex TUI process under iTerm2.

So I think the candidate's default-foreground idea is the right foundation, with the narrower Insert/Delete scope, assigned modifier results, and style-aware snapshot coverage added.