Skip to content

fix(ui): apply themed background to input area and app surface#2171

Closed
HUQIANTAO wants to merge 4 commits into
esengine:mainfrom
HUQIANTAO:fix/iterm-theme-background
Closed

fix(ui): apply themed background to input area and app surface#2171
HUQIANTAO wants to merge 4 commits into
esengine:mainfrom
HUQIANTAO:fix/iterm-theme-background

Conversation

@HUQIANTAO

@HUQIANTAO HUQIANTAO commented May 28, 2026

Copy link
Copy Markdown
Contributor

Summary

Three issues making the TUI unusable on iTerm (and any terminal with a non-default background):

1. Themed backgrounds not applied

The theme system defines SURFACE.bgInput (#0f172a) and SURFACE.bg (#0b1020) but no component applies them. The entire UI renders with the terminal's default background, making input text invisible and the theme ineffective.

  • PromptInput.tsx — add backgroundColor={SURFACE.bgInput} to the outer Box
  • App.tsx — add SURFACE import and backgroundColor={SURFACE.bg} to root content layout
  • ComposerArea.tsx — add SURFACE import and backgroundColor={SURFACE.bgInput} to wrapper Box

2. WCAG AA contrast failures on fg.faint and fg.meta

fg.faint failed WCAG on all 4 dark themes (ratios 1.63–2.56:1, need 4.5:1). fg.meta failed on midnight and was borderline on others. Dark theme bgElev was too close to bgInput for surface distinction.

Theme meta faint bgElev
dark #778294→#9aa5b5 #4d5666→#8791a3 #151d2f→#1c2844
light #6b7280→#5c6371 #9ca3af→#666d7b
midnight #565f89→#9da5bb #414868→#8a92a8
deep-blue #808080→#909090 #606060→#8c8c8c
high-contrast

3. ANSI dim causing invisible text across all UI components

The dim attribute (SGR 2) halves the brightness of the foreground color. On dark terminals, dimmed text without an explicit color uses the terminal default at half brightness — effectively invisible. This affects 22 components across the entire TUI.

Initial fix (earlier commits):

  • ReasoningCard.tsx — replace dim with explicit FG.sub for card metadata
  • ToolCard.tsx — replace conditional dim with explicit FG.faint/errColor
  • chat.tsx — emit OSC 11 to set terminal default background, reset via OSC 111 on exit

Full fix (this commit): Replace every remaining dim prop across 22 UI components with explicit color={FG.faint} or conditional color logic:

  • mcp-browse, AtMentionSuggestions, DiffApp, EditConfirm
  • McpBrowser, McpHub, McpMarketplace
  • PlanReviseConfirm, PlanStepList
  • RecordView, ReplayApp, Select, Setup
  • SlashArgPicker, SlashSuggestions, SplitDiff
  • StatsPanel, Wizard, char-bar, ctx-breakdown
  • markdown-view (SpanText ambientDim), primitives (Bar, ContextCell)

For conditional dim (e.g. dim={isDone}), replaced with conditional color logic (color={isDone ? FG.faint : originalColor}). The Bar component's dim prop was removed entirely — callers now pass the appropriate color directly.

Test plan

  • Verify input area has a distinct themed background in iTerm
  • Verify text is clearly readable against the new background
  • Switch themes with /theme — backgrounds and text should change accordingly
  • Verify border, placeholder, and faint text are visible with sufficient contrast
  • Verify reasoning card metadata ("◆ reasoning · 20 tok") is readable
  • Verify tool card preview lines are readable
  • Verify no white/empty areas below content in iTerm
  • Verify all UI components (slash suggestions, session picker, MCP browser, etc.) have readable text
  • Run npm run verify (lint + typecheck + tests)

HUQIANTAO added 2 commits May 28, 2026 22:58
The theme system defines SURFACE.bgInput (#0f172a) and SURFACE.bg
(#0b1020) but no component applies them. The PromptInput Box,
ComposerArea wrapper, and root App layout all lack backgroundColor
props, so the entire UI renders with the terminal's default background.

On iTerm (and any terminal whose default background differs from the
theme), this makes input text unreadable and the theme invisible.

- PromptInput: add backgroundColor={SURFACE.bgInput} to outer Box
- App: add backgroundColor={SURFACE.bg} to root content layout
- ComposerArea: add backgroundColor={SURFACE.bgInput} to wrapper
… themes

fg.faint failed WCAG on all 4 dark themes (ratios 1.63-2.56:1, need 4.5:1).
fg.meta failed on midnight (2.35-2.91:1) and was borderline on others.
Dark theme bgElev (#151d2f) was too close to bgInput for surface distinction.

Changes per theme:
- dark: meta #778294→#9aa5b5, faint #4d5666→#8791a3, bgElev #151d2f→#1c2844
- light: meta #6b7280→#5c6371, faint #9ca3af→#666d7b
- midnight: meta #565f89→#9da5bb, faint #414868→#8a92a8
- deep-blue: meta #808080→#909090, faint #606060→#8c8c8c
- high-contrast: already passes, no changes
@HUQIANTAO HUQIANTAO marked this pull request as draft May 28, 2026 15:25
HUQIANTAO and others added 2 commits May 28, 2026 23:31
…nd via OSC 11

- ReasoningCard: replace `dim` prop with explicit `FG.sub` color so the
  "reasoning · N tok · N ¶ · N.Ns" metadata is readable on dark terminals
- ToolCard: replace conditional `dim` with explicit `FG.faint`/`errColor`
  so tool output preview lines don't vanish on dark backgrounds
- chat.tsx: emit OSC 11 (`ESC]11;#rrggbb BEL`) before render to set the
  terminal's default background to the theme's surface.bg, eliminating
  white/empty areas below Ink's content in iTerm and other terminals
  that don't inherit the theme background; reset via OSC 111 on exit

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Replace every remaining `dim` prop across 22 UI components with explicit
`color={FG.faint}` (or conditional color logic where dim was used as a
boolean toggle). The ANSI `dim` attribute (SGR 2) halves brightness of
the foreground color, causing near-invisible text on dark terminals
where the default foreground is already low-contrast.

Components fixed:
- mcp-browse, AtMentionSuggestions, DiffApp, EditConfirm
- McpBrowser, McpHub, McpMarketplace
- PlanReviseConfirm, PlanStepList
- RecordView, ReplayApp, Select, Setup
- SlashArgPicker, SlashSuggestions, SplitDiff
- StatsPanel, Wizard, char-bar, ctx-breakdown
- markdown-view (SpanText ambientDim), primitives (Bar, ContextCell)

For conditional dim (e.g. `dim={isDone}`), replaced with conditional
color logic (`color={isDone ? FG.faint : originalColor}`). The Bar
component's `dim` prop was removed entirely — callers now pass the
appropriate color directly.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
@HUQIANTAO HUQIANTAO marked this pull request as ready for review May 28, 2026 16:09
@esengine

Copy link
Copy Markdown
Owner

This looks like it overlaps #2182 (same title, same author, same goal — themed background on the app surface/input). #2171 is the broad version (+217/-188, 'three issues'); #2182 is a focused +10/-4 that just applies SURFACE.bg to the root layout. Please pick one and close the other so they don't conflict — if the extra two issues in this PR are still needed, fold them into #2182 (or vice versa) and close the duplicate. Which one do you want to carry forward?

@HUQIANTAO

Copy link
Copy Markdown
Contributor Author

Closing — the background fix portion overlaps with #2182 (focused +10/-4). The WCAG contrast, OSC 11 terminal background, and ANSI dim→FG.faint changes are being extracted into a separate PR.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants