How gh-stack Auto-Detects Your Terminal Theme (Light vs Dark)

gh-stack detects your terminal background automatically through Bubble Tea's initialization routine, which queries the terminal's color capabilities and sets Lipgloss's background flag, while the theme package uses AdaptiveColor values that switch between light and dark hex codes at runtime.

The github/gh-stack CLI tool provides a seamless visual experience by automatically adapting its color palette to match your terminal's background. This gh-stack auto-detect terminal theme capability relies on two interconnected components from the Charm Bracelet ecosystem: Bubble Tea's startup detection and Lipgloss's adaptive rendering system. Understanding this mechanism explains why the tool renders correctly in both light and dark terminal environments without manual configuration.

How Terminal Theme Detection Works in gh-stack

Bubble Tea Initialization and Background Queries

When you launch any interactive gh-stack command, the underlying Bubble Tea framework executes its internal init routine located in bubbletea/tea/tea_init.go. This routine performs terminfo-style checks to determine whether the terminal background is dark or light. Once detected, it calls lipgloss.SetHasDarkBackground(bool) to cache the result for the entire command execution. This detection happens once per command because every gh-stack subcommand imports Bubble Tea, ensuring consistent theming throughout the session.

Lipgloss AdaptiveColor Runtime Selection

The internal/theme/theme.go file defines all UI colors as lipgloss.AdaptiveColor structs, each containing distinct Dark and Light hex values. During rendering, Lipgloss automatically selects the appropriate variant based on the background flag set during Bubble Tea initialization. If the terminal cannot respond to background queries—common in SSH sessions or tmux configurations—Lipgloss defaults to the dark palette to ensure readability.

Overriding Auto-Detection with GH_STACK_THEME

While automatic detection works for most modern terminals, gh-stack provides the GH_STACK_THEME environment variable for explicit control. Set this to light, dark, or auto to override the detected value. The override applies early in execution via theme.ApplyOverride(), which reads the environment variable and forces the palette before any output renders.

// Force a specific theme before any TUI or output renders
os.Setenv("GH_STACK_THEME", "light")
theme.ApplyOverride()

// Now all subsequent output uses the light palette
fmt.Println(theme.Success("✔ Stack created successfully"))

Implementation in the Theme Package

The theme system centralizes color definitions in internal/theme/theme.go, providing semantic colors like theme.Success, theme.Warning, and theme.ColorAccent. These wrap lipgloss.AdaptiveColor instances that automatically resolve to the correct hex values based on the current background detection state. The internal/tui/shared/theme.go file provides additional UI-specific helpers for Bubble Tea components.

// Apply an explicit theme before any output is rendered.
// This reads GH_STACK_THEME and forces the palette if needed.
theme.ApplyOverride()

// Use the adaptive colours in plain (non-TUI) output.
fmt.Println(theme.Success("✔  Stack applied successfully"))
fmt.Println(theme.Warning("⚠️  Rebase conflict detected"))
fmt.Println(theme.Blue("ℹ️  Run `gh stack view` for details"))

// In a Bubble Tea view you can just use the AdaptiveColours directly:
style := lipgloss.NewStyle().
    Foreground(theme.ColorAccent) // auto-selects Light/Dark
fmt.Println(style.Render("Submit"))

Summary

  • Bubble Tea handles detection: The init routine in bubbletea/tea/tea_init.go queries terminal capabilities and sets lipgloss.SetHasDarkBackground(bool) at startup.
  • Lipgloss manages switching: All colors in internal/theme/theme.go use lipgloss.AdaptiveColor with separate hex values for light and dark backgrounds.
  • Robust defaults: When detection fails (SSH, tmux, or older terminals), the system defaults to the dark palette.
  • User override: Set GH_STACK_THEME to light, dark, or auto and call theme.ApplyOverride() to bypass automatic detection.
  • Single initialization: Detection occurs once per command execution and caches the result for consistent theming across all output.

Frequently Asked Questions

What happens if gh-stack cannot detect my terminal background?

If the terminal does not respond to background queries—common in SSH sessions, Docker containers, or multiplexers like tmux—Lipgloss defaults to the dark color palette. This ensures text remains readable against unknown background colors, though you can force the light theme by setting GH_STACK_THEME=light.

Can I force a specific theme without changing my terminal settings?

Yes. Export GH_STACK_THEME with values light, dark, or auto in your shell environment. The theme.ApplyOverride() function reads this variable early in the application lifecycle and forces the corresponding palette, bypassing Bubble Tea's auto-detection entirely.

Does theme detection work in CI/CD pipelines or non-interactive environments?

The detection mechanism relies on terminal capability queries that often return false or default values in non-interactive environments. In these cases, gh-stack defaults to the dark theme. For CI pipelines requiring specific colors, explicitly set GH_STACK_THEME to ensure consistent output formatting.

Where are the color definitions stored in the gh-stack repository?

All color definitions reside in internal/theme/theme.go. This file contains the AdaptiveColor instances with hex codes for both light and dark variants, along with the ApplyOverride() function that handles the GH_STACK_THEME environment variable logic.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →