Skip to content

Proposal: Unified theming system across charmbracelet libraries #643

@junhinhow

Description

@junhinhow

Problem

Each charmbracelet library has its own styling system:

  • lipgloss uses lipgloss.Style with inline color definitions
  • glamour uses StyleConfig with JSON or Go structs
  • bubbles components each have their own Styles struct

When building a TUI app, you end up defining the same colors 3+ times in different formats. Changing your theme means updating every single style definition across libraries.

Proposed Solution

A cross-library theming package that defines semantic color palettes once, then derives styles for any charmbracelet library.

import themes "github.com/junhinhow/charm-themes"

// Pick a theme
theme := themes.Gruvbox

// Use with lipgloss — pre-built semantic styles
styles := theme.Lipgloss()
fmt.Println(styles.Title.Render("Hello"))
fmt.Println(styles.Error.Render("Something went wrong"))
fmt.Println(styles.Border.Render("Boxed content"))

// Or use colors directly in custom styles
myStyle := lipgloss.NewStyle().
    Foreground(theme.Primary).
    Background(theme.Background)

Theme Structure

type Theme struct {
    Name       string
    Background color.Color
    Foreground color.Color
    Primary    color.Color  // headings, titles
    Secondary  color.Color  // subtitles, secondary info
    Accent     color.Color  // highlights, emphasis
    Success    color.Color  // green/positive
    Warning    color.Color  // yellow/caution
    Error      color.Color  // red/negative
    Info       color.Color  // blue/informational
    Muted      color.Color  // gray/disabled
    Syntax     SyntaxColors // code highlighting colors
}

Built-in Themes

Theme Background Primary Accent
Gruvbox #282828 #fabd2f #83a598
Dracula #282a36 #bd93f9 #ff79c6
Nord #2e3440 #88c0d0 #b48ead
Catppuccin #1e1e2e #cba6f7 #f5c2e7
Tokyo Night #1a1b26 #7aa2f7 #bb9af7

POC Repository

I've built a working proof-of-concept with lipgloss integration:
👉 https://github.com/junhinhow/charm-themes

Currently supports:

  • 5 built-in themes with complete palettes
  • theme.Lipgloss() → pre-built LipglossStyles (Base, Bold, Title, Error, Border, Code, etc.)
  • Direct access to semantic color.Color values for custom styles

Roadmap (if there's interest)

  • theme.Glamour() → convert to glamour.StyleConfig + Chroma palette
  • theme.Bubbles() → pre-built styles for table, list, textinput, etc.
  • theme.Huh() → form theme integration
  • Light/dark variant detection via lipgloss.HasDarkBackground()
  • Custom theme loading from JSON/YAML

Questions for the team

  1. Would this be useful as an official charmbracelet package (e.g. charm.land/themes)?
  2. Or better as a community package that integrates with the ecosystem?
  3. Any feedback on the Theme struct design — are the semantic color roles sufficient?

Happy to adapt the approach based on your feedback. This came from the experience of building apps with the full Charm stack and repeatedly copy-pasting color values across lipgloss, glamour, and bubbles configs.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions