Files
cburn/internal/tui/theme/theme.go
teernisse 253776ffd5 feat: add TUI theme system with four color palettes
Implement the theming layer for the interactive dashboard:

- tui/theme/theme.go: Theme struct with 14 semantic color roles
  (Background, Surface, Border, BorderHover, TextDim, TextMuted,
  TextPrimary, Accent, Green, Orange, Red, Blue, Purple, Yellow).
  Four built-in themes:

  * Flexoki Dark (default): warm, low-contrast palette designed for
    extended reading. Teal accent (#3AA99F) on near-black background.

  * Catppuccin Mocha: pastel warm tones with blue accent (#89B4FA)
    on dark surface (#1E1E2E).

  * Tokyo Night: cool blue/purple palette with blue accent (#7AA2F7)
    on deep navy (#1A1B26).

  * Terminal: ANSI 16-color only for maximum compatibility — maps
    all roles to standard terminal colors (0-15), works correctly
    in any terminal emulator regardless of true-color support.

  Global Active variable holds the current theme. SetActive/ByName
  enable runtime theme switching from the settings tab and setup
  wizard.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-19 13:02:45 -05:00

120 lines
3.5 KiB
Go

package theme
import "github.com/charmbracelet/lipgloss"
// Theme defines the color roles used throughout the TUI.
type Theme struct {
Name string
Background lipgloss.Color
Surface lipgloss.Color
Border lipgloss.Color
BorderHover lipgloss.Color
TextDim lipgloss.Color
TextMuted lipgloss.Color
TextPrimary lipgloss.Color
Accent lipgloss.Color
Green lipgloss.Color
Orange lipgloss.Color
Red lipgloss.Color
Blue lipgloss.Color
Purple lipgloss.Color
Yellow lipgloss.Color
}
// Active is the currently selected theme.
var Active = FlexokiDark
// FlexokiDark is the default theme.
var FlexokiDark = Theme{
Name: "flexoki-dark",
Background: lipgloss.Color("#100F0F"),
Surface: lipgloss.Color("#1C1B1A"),
Border: lipgloss.Color("#282726"),
BorderHover: lipgloss.Color("#343331"),
TextDim: lipgloss.Color("#575653"),
TextMuted: lipgloss.Color("#6F6E69"),
TextPrimary: lipgloss.Color("#FFFCF0"),
Accent: lipgloss.Color("#3AA99F"),
Green: lipgloss.Color("#879A39"),
Orange: lipgloss.Color("#DA702C"),
Red: lipgloss.Color("#D14D41"),
Blue: lipgloss.Color("#4385BE"),
Purple: lipgloss.Color("#8B7EC8"),
Yellow: lipgloss.Color("#D0A215"),
}
// CatppuccinMocha is a warm pastel theme.
var CatppuccinMocha = Theme{
Name: "catppuccin-mocha",
Background: lipgloss.Color("#1E1E2E"),
Surface: lipgloss.Color("#313244"),
Border: lipgloss.Color("#45475A"),
BorderHover: lipgloss.Color("#585B70"),
TextDim: lipgloss.Color("#6C7086"),
TextMuted: lipgloss.Color("#A6ADC8"),
TextPrimary: lipgloss.Color("#CDD6F4"),
Accent: lipgloss.Color("#89B4FA"),
Green: lipgloss.Color("#A6E3A1"),
Orange: lipgloss.Color("#FAB387"),
Red: lipgloss.Color("#F38BA8"),
Blue: lipgloss.Color("#89B4FA"),
Purple: lipgloss.Color("#CBA6F7"),
Yellow: lipgloss.Color("#F9E2AF"),
}
// TokyoNight is a cool blue/purple theme.
var TokyoNight = Theme{
Name: "tokyo-night",
Background: lipgloss.Color("#1A1B26"),
Surface: lipgloss.Color("#24283B"),
Border: lipgloss.Color("#414868"),
BorderHover: lipgloss.Color("#565F89"),
TextDim: lipgloss.Color("#565F89"),
TextMuted: lipgloss.Color("#A9B1D6"),
TextPrimary: lipgloss.Color("#C0CAF5"),
Accent: lipgloss.Color("#7AA2F7"),
Green: lipgloss.Color("#9ECE6A"),
Orange: lipgloss.Color("#FF9E64"),
Red: lipgloss.Color("#F7768E"),
Blue: lipgloss.Color("#7AA2F7"),
Purple: lipgloss.Color("#BB9AF7"),
Yellow: lipgloss.Color("#E0AF68"),
}
// Terminal uses ANSI 16 colors only.
var Terminal = Theme{
Name: "terminal",
Background: lipgloss.Color("0"),
Surface: lipgloss.Color("0"),
Border: lipgloss.Color("8"),
BorderHover: lipgloss.Color("7"),
TextDim: lipgloss.Color("8"),
TextMuted: lipgloss.Color("7"),
TextPrimary: lipgloss.Color("15"),
Accent: lipgloss.Color("6"),
Green: lipgloss.Color("2"),
Orange: lipgloss.Color("3"),
Red: lipgloss.Color("1"),
Blue: lipgloss.Color("4"),
Purple: lipgloss.Color("5"),
Yellow: lipgloss.Color("3"),
}
// All available themes.
var All = []Theme{FlexokiDark, CatppuccinMocha, TokyoNight, Terminal}
// ByName returns a theme by its name, defaulting to FlexokiDark.
func ByName(name string) Theme {
for _, t := range All {
if t.Name == name {
return t
}
}
return FlexokiDark
}
// SetActive sets the active theme by name.
func SetActive(name string) {
Active = ByName(name)
}