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

> Discover how gh-stack auto-detects your terminal theme. Learn how it dynamically adjusts to light or dark modes for an optimal viewing experience.

- Repository: [GitHub/gh-stack](https://github.com/github/gh-stack)
- Tags: internals
- Published: 2026-08-03

---

**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`](https://github.com/github/gh-stack/blob/main/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`](https://github.com/github/gh-stack/blob/main/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.

```go
// 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`](https://github.com/github/gh-stack/blob/main/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`](https://github.com/github/gh-stack/blob/main/internal/tui/shared/theme.go) file provides additional UI-specific helpers for Bubble Tea components.

```go
// 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`](https://github.com/github/gh-stack/blob/main/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`](https://github.com/github/gh-stack/blob/main/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`](https://github.com/github/gh-stack/blob/main/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.