How witr Adapts Color Themes for Light and Dark Terminals

witr uses the Lipgloss library's AdaptiveColor type to automatically select appropriate color values based on the terminal's detected background, ensuring high contrast without user configuration.

The open-source terminal UI tool witr (by pranshuparmar/witr) solves the challenge of maintaining readable interfaces across different terminal themes. By leveraging adaptive color definitions in internal/tui/theme.go, the application renders darker variants on light backgrounds and brighter variants on dark backgrounds at runtime.

Lipgloss AdaptiveColor Implementation

The core mechanism resides in internal/tui/theme.go, where every foreground color is declared as a lipgloss.AdaptiveColor struct. This type accepts two hex values: Light for light-terminal backgrounds and Dark for dark-terminal backgrounds.

// internal/tui/theme.go – adaptive color definition
var (
    // Accent color for table headers, prompts, and active borders
    colorAccent = lipgloss.AdaptiveColor{
        Light: "#4338ca", // Darker indigo for light terminals
        Dark:  "#5f5fd7", // Brighter purple for dark terminals
    }
)

When the UI renders, Lipgloss inspects the terminal environment and automatically selects the appropriate field. This detection happens at render time, meaning the same binary produces different visual outputs depending on the user's terminal settings without requiring configuration flags.

Fixed Colors for Background Elements

Not all colors require adaptation. Elements that define their own backgrounds—such as the brand bar, selected rows, and tabs—use plain lipgloss.Color values. These components maintain consistent background colors regardless of terminal theme because they control their own contrast context.

// Example of a non-adaptive background color
brandBackground := lipgloss.NewStyle().
    Background(lipgloss.Color("#1e1e2e")).
    Padding(0, 2)

This distinction prevents visual inconsistencies where background colors might clash with adaptive foreground text.

Applying Themes in the UI Layer

The internal/tui/view.go file consumes these theme definitions when constructing the interface. Styles are composed using the adaptive color variables, allowing borders, text, and highlights to shift automatically.

// internal/tui/view.go – applying adaptive colors
headerStyle := lipgloss.NewStyle().
    BorderForeground(colorAccent).
    Bold().
    Padding(0, 1)

// Render call automatically resolves to Light or Dark variant
fmt.Println(headerStyle.Render("Active Processes"))

This pattern extends to muted text, error messages, and border accents, ensuring the entire interface maintains consistent contrast ratios across terminal environments.

Summary

  • Automatic Detection: witr relies on Lipgloss's internal terminal background detection to select between Light and Dark color variants at runtime.
  • AdaptiveColor Usage: Foreground colors in internal/tui/theme.go use lipgloss.AdaptiveColor with explicit hex values for both light (#4338ca) and dark (#5f5fd7) modes.
  • Fixed Backgrounds: UI elements that render their own backgrounds use static lipgloss.Color values to prevent contrast conflicts.
  • Zero Configuration: The adaptation requires no user intervention or runtime flags, functioning immediately across different terminal emulators.

Frequently Asked Questions

How does witr detect whether my terminal uses a light or dark theme?

witr delegates detection to the Lipgloss library (imported via github.com/charmbracelet/lipgloss in go.mod), which queries the terminal environment and background color settings at render time to determine which color variant to apply.

Can I customize the light or dark color values in witr?

Currently, the color values are hardcoded in internal/tui/theme.go as constants within the lipgloss.AdaptiveColor structs. Modifying these hex values requires editing the source code and recompiling the application.

Why do some UI elements look the same in both light and dark terminals?

Background-drawing elements like the brand bar, selected row highlights, and tab backgrounds use plain lipgloss.Color rather than AdaptiveColor. These components maintain fixed colors because they establish their own contrast context and do not need to adapt to the terminal background.

Which file should I examine to change witr's color scheme?

All color definitions reside in internal/tui/theme.go. This file contains the lipgloss.AdaptiveColor declarations for foreground elements and static lipgloss.Color values for background elements used throughout the application.

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 →