Skip to content

Adapt theme colors to light and dark terminals#149

Open
skarim wants to merge 2 commits into
skarim/submit-tuifrom
skarim/tui-colors
Open

Adapt theme colors to light and dark terminals#149
skarim wants to merge 2 commits into
skarim/submit-tuifrom
skarim/tui-colors

Conversation

@skarim

@skarim skarim commented Jun 26, 2026

Copy link
Copy Markdown
Collaborator

gh-stack's colors — both the interactive TUIs (submit, view, modify) and plain command output (status messages, prompts) — were tuned for dark terminals. On light or solarized backgrounds they read poorly: TUI text used ANSI white (invisible on white) with inverted emphasis, and command output used fixed ANSI palette colors that never adapted.

This PR introduces one background-aware color palette and uses it everywhere:

  • internal/theme: a foundational package holding the adaptive lipgloss.AdaptiveColor palette (semantic roles: text/muted/faint, chrome, accent, PR-state colors, …), the GH_STACK_THEME override, and colorizers for plain output. lipgloss resolves the light/dark variant per render from the detected terminal background; terminals that don't report it fall back to dark, preserving the original look.
  • TUIs (submit/view/modify): every hardcoded ANSI color becomes a palette role; pre-rendered status icons render at use-time, and the submit markdown preview picks glamour's light/dark style.
  • Command output: config and the prompt helpers drop mgutz/ansi and color via the shared palette, so success/error/warning messages and prompts adapt too.
  • GH_STACK_THEME=auto|light|dark forces the palette for terminals that mis-detect (some SSH/tmux setups). Detection is free — Bubble Tea's startup background query (already run for every command) is cached.

Neutral colors use truecolor hex (GitHub Primer-inspired) for consistency across themes including solarized, which repurposes ANSI 8–15; lipgloss downsamples on terminals without truecolor. Tests cover the palette's light/dark resolution, the GH_STACK_THEME override, and that command output colors adapt.

image

Stack created with GitHub Stacks CLIGive Feedback 💬

Copilot AI review requested due to automatic review settings June 26, 2026 23:02
GitHub Advanced Security started work on behalf of skarim June 26, 2026 23:06 View session
GitHub Advanced Security finished work on behalf of skarim June 26, 2026 23:07

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Pull request overview

This PR makes the submit, view, and modify interactive TUIs readable on both light and dark terminal backgrounds. Previously colors were hardcoded ANSI indices tuned only for dark terminals (e.g. ANSI white was invisible on light backgrounds, accents were low-contrast). It introduces a centralized, background-aware palette of lipgloss.AdaptiveColor roles and migrates every hardcoded color across shared/, submitview/, and modifyview/ to it.

Changes:

  • New internal/tui/shared/theme.go defines a semantic, GitHub Primer-inspired palette (text/muted/faint, border, accent, PR-state, badge, button, switch) using lipgloss.AdaptiveColor, plus ApplyThemeOverride() honoring GH_STACK_THEME=auto|light|dark, wired via the root command's PersistentPreRun.
  • Replaces hardcoded ANSI colors with palette roles everywhere; status icons now render at use-time so adaptive colors resolve against the detected background, and the markdown preview picks glamour's light/dark style from lipgloss.HasDarkBackground().
  • Adds tests (theme_test.go) verifying the palette differs by background and that GH_STACK_THEME is honored, plus README and CLI docs.
Show a summary per file
File Description
internal/tui/shared/theme.go New adaptive palette + ApplyThemeOverride() for GH_STACK_THEME.
internal/tui/shared/theme_test.go Tests palette background-awareness and theme override.
internal/tui/shared/styles.go Migrates branch/PR/diff/connector/header styles to palette; icons rendered at use-time.
internal/tui/shared/render.go StatusIcon renders glyph+style at use-time instead of pre-rendered icons.
internal/tui/shared/header.go Disabled-shortcut style uses ColorTextFaint.
internal/tui/submitview/styles.go State fg/bg, chrome, switch, segment styles moved to palette; Color() returns TerminalColor.
internal/tui/submitview/screen.go Branch circle/name/checkbox + left panel border use palette.
internal/tui/submitview/editor.go Field/desc box borders use palette accent/border.
internal/tui/submitview/render.go Chrome + "new PRs" highlight use palette.
internal/tui/submitview/preview.go Selects light/dark glamour style from detected background.
internal/tui/submitview/help.go Help overlay styles use palette.
internal/tui/modifyview/styles.go Action/connector/status/help/transient styles use palette.
internal/tui/modifyview/model.go Pending-summary icon uses ColorYellow.
cmd/root.go PersistentPreRun applies the theme override before any render.
README.md, docs/.../cli.md Document GH_STACK_THEME and auto-adaptation behavior.

Review details

  • Files reviewed: 16/16 changed files
  • Comments generated: 0
  • Review effort level: Medium

The submit, view, and modify TUIs were tuned for dark terminals. On light
or solarized-light backgrounds the result was hard to read and inverted:
primary text used ANSI white (invisible on white), dim chrome used light
grays (too faint), and accents used bright cyan (low contrast) — so
"active" things looked lighter than "disabled" ones.

Introduce a centralized, background-aware color palette and migrate all
three TUIs to it:

- internal/tui/shared/theme.go: a semantic palette of lipgloss.AdaptiveColor
  values (primary/muted/faint text, chrome/border, accent, PR-state colors,
  badge backgrounds, row shade, button, switch). lipgloss resolves the
  light/dark variant per render from the terminal background, which Bubble
  Tea detects at startup; terminals that don't report it fall back to dark,
  preserving the original look.
- Replace every hardcoded ANSI color in shared/, submitview/, and
  modifyview/ with palette roles. The four pre-rendered status icons now
  render at use-time so their adaptive colors resolve correctly. The submit
  markdown preview picks glamour's light or dark style from the detected
  background.
- GH_STACK_THEME=auto|light|dark forces the palette for terminals that
  mis-detect (some SSH/tmux setups); wired via the root command's
  PersistentPreRun before any render. Documented in the README and CLI docs.

Neutral text/chrome use truecolor hex (GitHub Primer-inspired) for
predictability across themes, including solarized which repurposes ANSI
8-15; lipgloss downsamples on terminals without truecolor.

Tests verify the palette resolves differently for light vs dark and that
GH_STACK_THEME is honored.
@skarim skarim force-pushed the skarim/tui-colors branch from d9ed2c3 to 6a41f54 Compare June 28, 2026 14:36
GitHub Advanced Security started work on behalf of skarim June 28, 2026 14:36 View session
GitHub Advanced Security started work on behalf of skarim June 28, 2026 14:36 View session
GitHub Advanced Security finished work on behalf of skarim June 28, 2026 14:37
GitHub Advanced Security finished work on behalf of skarim June 28, 2026 14:37
Background detection and the GH_STACK_THEME override (added for the TUIs)
only affected the interactive screens. Plain command output -- status
messages and interactive prompts -- went through the mgutz/ansi library
with fixed ANSI palette names (green/red/yellow/cyan/...), so it never
adapted to the terminal background and could read poorly on light or
solarized themes.

Unify everything on the same adaptive palette so all colors react to the
detected background and to GH_STACK_THEME.

- Extract internal/theme, a foundational package with no internal
  dependencies, that owns:
    - the background-aware lipgloss.AdaptiveColor palette (moved out of
      internal/tui/shared),
    - ApplyOverride(), the GH_STACK_THEME=auto|light|dark logic, and
    - non-TUI colorizers (Success/Error/Warning/Blue/Magenta/Cyan/Gray/
      Bold) plus FgSeqs(), which returns the raw start/reset escapes used
      to color the user's echoed prompt input.
- internal/tui/shared/theme.go now re-exports the palette, so the TUI code
  keeps referring to shared.ColorX unchanged.
- internal/config/config.go wires the Config.Color* funcs to the theme
  colorizers and drops mgutz/ansi (now an indirect dependency only).
- cmd/utils.go colors the prompt icon and echoed input via theme.
- cmd/root.go calls theme.ApplyOverride() in PersistentPreRun.

Detection adds no cost: because the command package imports Bubble Tea,
its init() already triggers (and caches) the terminal background query for
every command, so the non-TUI colorizers just read the cached value.
Terminals that don't answer the query fall back to the dark palette;
GH_STACK_THEME=light|dark forces it. Colors are truecolor on capable
terminals and downsample to the nearest ANSI color elsewhere.

Tests: internal/theme covers palette adaptiveness, ApplyOverride, the
colorizers, and FgSeqs; a new internal/config test verifies the wired-up
Config.Color* funcs adapt to the background when color is enabled.

Docs: README and the CLI reference note that GH_STACK_THEME now controls
all colored output, not just the interactive screens.

No behavior change beyond colors.
GitHub Advanced Security started work on behalf of skarim June 28, 2026 18:28 View session
GitHub Advanced Security finished work on behalf of skarim June 28, 2026 18:29
@skarim skarim changed the title Adapt TUIs to light and dark terminal backgrounds Adapt theme colors to light and dark terminals Jun 28, 2026
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