Skip to content

fix(tui): repair light/dark theme switching artifacts - #196

Merged
Desperado merged 2 commits into
mainfrom
fix/theme-contrast-mismatch
Sep 12, 2026
Merged

Desperado merged 2 commits into
mainfrom
fix/theme-contrast-mismatch

Conversation

@Desperado

Copy link
Copy Markdown
Contributor

Follow-up to #195, which made light↔dark switching look broken.

Problem

After /theme switches between light and dark themes, the UI showed gray/black cutout blocks: the picker surface kept one polarity while row styles flipped to the other, and stale cells from the previous theme lingered on screen.

Root cause

  1. fix(tui): enforce backgrounds on light themes to prevent invisible text #195 added opaque SurfaceDark backgrounds to nested containers (pickerBox, inputBoxStyle). Nested styled spans emit ANSI reset sequences that interrupt the inherited parent background, so the terminal's native background punches through around every styled label — visible as cutout blocks on any polarity mismatch.
  2. On theme transitions, Bubble Tea only repaints the cells its frame covers; cells painted by the previous polarity stayed on screen.

Fix

  • Structural containers (pickerBox, inputBoxStyle) are transparent again; only leaf surfaces (statusBarStyle, statusMetricsStyle) keep solid backgrounds — they have no nested styled children, so resets cannot cut holes in them.
  • Theme picker previews force tea.ClearScreen on every navigation step and on Escape-cancel, so the whole visible frame is repainted in the new polarity instead of leaving stale cells.

Known limitation

Text already printed into scrollback keeps the colors it was printed with — ANSI cannot restyle scrolled-out history; only the visible frame is repainted. This is inherent to terminals, not fixable in-app.

Tests

  • TestApplyTheme_BackgroundOwnership — containers transparent, leaf surfaces solid, across all 10 themes
  • TestThemePicker_PolarityTransitionsRequestFullRepaint — dark→light→dark preview transitions must emit a full-repaint command
  • Updated TestThemePalette_SelectionContrastsSurface rationale

Verification

go test ./internal/tui, go test -race ./internal/tui, go vet ./..., release-style build, and git diff --check all pass.

- Keep structural containers (pickerBox, inputBoxStyle) transparent:
  nested styled spans emit ANSI resets that punch through inherited
  parent backgrounds, causing gray/black cutout blocks
- Force full repaint (tea.ClearScreen) on theme-picker navigation and
  Escape-cancel so stale cells from the previous polarity cannot linger
- Replace background regression tests: containers must be transparent,
  leaf surfaces (status bar, metrics) stay solid
- Add dark-to-light-to-dark preview transition test asserting repaint
@qualitymaxapp

qualitymaxapp Bot commented Sep 12, 2026 •

Copy link
Copy Markdown

QualityMax Review

Verdict: COMMENT · Confidence: evidence-backed scan

Files eligible: 4 · Files reviewed: 4 · Files with findings: 0 · Findings: 0 · Inline cards: 0

Priority findings

priority location finding
— — No blocking findings

Review gates

gate status
AI diff review completed · eligible 4, reviewed 4 · LLM · served gemini-3.1-flash-lite
SAST completed · eligible 4, reviewed 4 · hybrid · served qwen3.7-plus
Overall review evidence clean
Inline evidence not needed

Important files

file risk note next step
— — No findings —

Change diagram — Flow

graph TD
    Input[User Input: Up/Down/Esc] --> Check{Polarity Changed?}
    Check -- Yes --> Clear[Return tea.ClearScreen]
    Check -- No --> Update[ApplyTheme Only]
    Clear --> Render[Repaint Screen]
    Update --> Render
Loading

Review lifecycle

Use the inline cards to inspect evidence and suggested remediation. Re-run the QualityMax review after pushing a fix; unchanged cards are identified by their stable finding marker. Dismiss with a reason through the existing QualityMax/GitHub review feedback flow. 0 prior card(s) are stale/resolved on this head. @qmax Q&A is tracked separately.

Proof legend: VERIFIED independently judged patch · REPRODUCED verified finding · GROUNDED deterministic evidence · MODEL-ONLY model judgment.

QualityMax project results are available in the configured project.

Receipt · commit f805c7cf2e1f7be78abd27649c2799604026d1f3 · run 2026-09-12T11:47:24+00:00 · model served qwen3.7-plus, gemini-3.1-flash-lite · model requested qwen3.7-plus, gemini-3.1-flash-lite · model review recorded — 1043 model output tokens · model source repository ai_review_preferences.preferred_model · re-review 2 · proof counts {}

@qualitymaxapp qualitymaxapp Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

QualityMax Review — canonical overview updated; inline findings are attached to this review.

@qualitymaxapp

qualitymaxapp Bot commented Sep 12, 2026 •

Copy link
Copy Markdown

⚠️ QualityMax Pipeline

Gate Result
🔍 AI diff review ✅ Clean · gemini-3.1-flash-lite · completed · 4 eligible / 4 reviewed · gemini-3.1-flash-lite
🔍 SAST completed · 4 eligible / 4 reviewed · qwen3.7-plus
🔍 Canonical PR review delivery completed · 0 eligible / 0 reviewed · exact-head review #5186325719 and overview #5645483524 confirmed
🧪 Repo Tests ✅ 802/802 passed (go)

Powered by QualityMax — AI-Powered Test Automation

Adversarial-review follow-ups:

- Foreground-only picker chrome (labels, headers, footers, dividers,
  hints) now keys off the terminal's actual background polarity instead
  of the previewed theme's. Previewing a light theme on a dark terminal
  (or vice versa) previously rendered near-black labels on a dark
  background — the exact invisible-text bug #195 tried to fix. Values
  match same-polarity themes exactly, so matched setups are unchanged.
- Gate tea.ClearScreen on actual polarity flips: clearing erases the
  visible screenful in place, so every arrow key was destroying the
  on-screen transcript. Same-polarity moves rely on the renderer's
  line diff; cancel only clears when the restore flips polarity.
- Skip glamour renderer rebuilds when polarity is unchanged.
- Tests: same-polarity move must not clear, no-flip cancel must quit
  without clear, chrome polarity adaptation, renderer rebuild elision.
@Desperado

Copy link
Copy Markdown
Contributor Author

Addressed the adversarial review:

  1. Invisible-chrome regression (blocker) — fg-only picker chrome (labels, headers, footers, dividers, hints) now follows the terminal's detected background polarity instead of the previewed theme's. Previewing a light theme on a dark terminal keeps labels readable; values are identical to same-polarity themes so matched setups render exactly as before. Styles that carry an explicit background (selected row, status bars, badges) still use the theme — fg/bg pairs stay internally consistent.
  2. Transcript erasure — tea.ClearScreen is now gated on actual polarity flips. Same-polarity arrow moves (the majority) no longer erase the visible screenful; cancel only clears when the restore flips polarity.
  3. Renderer rebuild elided when polarity is unchanged.

New tests cover all three; tui suite + race + vet + release build green.

@qualitymaxapp qualitymaxapp Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

QualityMax Review — canonical overview updated; inline findings are attached to this review.

@Desperado
Desperado merged commit b9ae28d into main Sep 12, 2026
5 of 6 checks passed
@Desperado
Desperado deleted the fix/theme-contrast-mismatch branch September 12, 2026 12:27
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant